reselect
Reselect es una biblioteca sencilla para crear funciones selectoras memorizadas y componibles. Los selectores Reselect se pueden utilizar para calcular eficientemente los datos derivados del store de Redux.
¿Por qué?
En el ejemplo anterior, mapStateToProps
llama a getVisibleTodos
para calcular los todos
. Esto funciona muy bien, pero hay un inconveniente: todos
se calcula cada vez que se actualiza el componente. Si el árbol de estado es grande o el cálculo es costoso, repetir el cálculo en cada actualización puede causar problemas de rendimiento. Reselect puede ayudar a evitar estos recálculos innecesarios.
Creación de un selector memorizados
Nos gustaría reemplazar getVisibleTodos
con un selector memorizados que recalcula todos
cuando el valor de state.todos
o state.visibilityFilter
cambia, pero no cuando ocurren cambios en otras partes (no relacionadas) del árbol de estado.
Reselect proporciona una función createSelector
para crear selectores memorizados. createSelector
toma una matriz de selectores de entrada y una función de transformación como sus argumentos. Si el árbol de estado Redux está mutado de una manera que hace que el valor de un selector de entrada cambie, el selector llamará a su función de transformación con los valores de los selectores de entrada como argumentos y devolverá el resultado. Si los valores de los selectores de entrada son iguales a los de la llamada anterior al selector, devolverá el valor previamente calculado en lugar de llamar a la función de transformación.
Vamos a definir un selector memorizado llamado getVisibleTodos
para reemplazar la versión no-memorizada anterior:
import { createSelector } from 'reselect'
const getVisibilityFilter = (state) => state.visibilityFilter
const getTodos = (state) => state.todos
export const getVisibleTodos = createSelector(
[ getVisibilityFilter, getTodos ],
(visibilityFilter, todos) => {
switch (visibilityFilter) {
case 'SHOW_ALL':
return todos
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
}
}
)
Componer selectores
Un selector memorizado puede ser un selector de entrada a otro selector memorizado. Vamos a usar getVisibleTodos
como un selector de entrada a un selector que filtra, adicionalmente, por palabra clave:
const getKeyword = (state) => state.keyword
const getVisibleTodosFilteredByKeyword = createSelector(
[ getVisibleTodos, getKeyword ],
(visibleTodos, keyword) => visibleTodos.filter(
todo => todo.text.indexOf(keyword) > -1
)
)
Conectar el selector a React
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'
const mapStateToProps = (state) => {
return {
todos: getVisibleTodos(state)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
Si necesitasemos acceder a las propiedades dentro del selector, deberemos modificar la función mapStateToProps
y los selectores
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter
const getTodos = (state, props) =>
state.todoLists[props.listId].todos
// ...
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}