Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I get ownProps using reselect on redux?

Tags:

I want to create a selector with memoization using reselect based on some ownProps of mapStateToProps.

like image 641
Sibelius Seraphini Avatar asked Apr 03 '16 22:04

Sibelius Seraphini


1 Answers

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

like image 140
Seth Avatar answered Oct 23 '22 13:10

Seth