I want to create a selector with memoization using reselect based on some ownProps
of mapStateToProps
.
You can do this by connecting the selector to a component using the connect
method provided by react-redux, then passing the component props (ownProps) as the second argument to the selector.
container.js
import { connect } from 'react-redux';
import { getVisibleTodos } from './selectors';
...
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props),
};
};
const VisibleTodoList = connect(
mapStateToProps,
)(TodoList);
export default VisibleTodoList;
You can then access those props in your selector
selectors.js
import { createSelector } from 'reselect';
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter;
const getTodos = (state, props) =>
state.todoLists[props.listId].todos;
const getVisibleTodos = createSelector(
...
);
export default getVisibleTodos;
However, this will not memoize correctly if you have multiple instances of the component you're passing props from. In that case, the selector would receive a different
props
argument each time, so it would always recompute instead of returning a cached value.
To share a selector across multiple components while passing in props and retaining memoization, each instance of the component needs its own private copy of the selector.
You can do this by creating a function that returns a new copy of the selector each time it's called.
selectors.js
import { createSelector } from 'reselect';
const getVisibilityFilter = (state, props) =>
state.todoLists[props.listId].visibilityFilter;
const getTodos = (state, props) =>
state.todoLists[props.listId].todos;
const makeGetVisibleTodos = () => {
return createSelector(
...
);
}
export default makeGetVisibleTodos;
If the mapStateToProps
argument supplied to connect returns a function instead of an object, it will be used to create an individual mapStateToProps
function for each instance of the container.
With that in mind, you can create a function makeMapStateToProps
that creates a new getVisibleTodos
selector, and returns a mapStateToProps
function that has exclusive access to the new selector:
import { connect } from 'react-redux';
import { makeGetVisibleTodos } from './selectors';
...
const makeMapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos();
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props),
};
};
return mapStateToProps;
};
const VisibleTodoList = connect(
makeMapStateToProps,
)(TodoList);
export default VisibleTodoList;
Now each instance of the VisibleTodosList
container will get its own mapStateToProps
function with a private getVisibleTodos
selector. Memoization will now work correctly regardless of the render order of the containers.
This was adapted (blatently copied) from the Reselect documentation