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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With