I am trying to integrate reselect
into my current app and as always , first i begin to read documentation and then if it needed , another recources.I couldn't understand one special part of documentation and also couldn't find recources which would explain in a more clear way.Now i am here to get some clear explanation .
So it says in documentation `
import React from 'react'
import Footer from './Footer'
import AddTodo from '../containers/AddTodo'
import VisibleTodoList from '../containers/VisibleTodoList'
const App = () => (
<div>
<VisibleTodoList listId="1" />
<VisibleTodoList listId="2" />
<VisibleTodoList listId="3" />
</div>
)
Using the getVisibleTodos selector with multiple instances of the VisibleTodoList container will not correctly memoize:
import { connect } from 'react-redux'
import { toggleTodo } from '../actions'
import TodoList from '../components/TodoList'
import { getVisibleTodos } from '../selectors'
const mapStateToProps = (state, props) => {
return {
// WARNING: THE FOLLOWING SELECTOR DOES NOT CORRECTLY MEMOIZE
todos: getVisibleTodos(state, props)
}
}
const mapDispatchToProps = (dispatch) => {
return {
onTodoClick: (id) => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
A selector created with createSelector has a cache size of 1 and only returns the cached value when its set of arguments is the same as its previous set of arguments. If we alternate between rendering
<VisibleTodoList listId="1" />
and<VisibleTodoList listId="2" />
, the shared selector will alternate between receiving{listId: 1}
and{listId: 2}
as its props argument. This will cause the arguments to be different on each call, so the selector will always recompute instead of returning the cached value.
Pay attention to last sentence . Why to return cached value if we pass different id
s and it should return us different values depends on id
s ?
So we have this selector getting state for our VisibleTodoList
component:
const mapStateToProps = (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
If we render the component:
return (
<div>
<VisibleTodoList listId="1" />
</div>
)
Then, the selector gets called like: getVisibleTodos(state, { listId: 1 })
, and the selector stores (memoizes) the result (todo list 1 object) in memory.
If we render the component twice with the same props:
return (
<div>
<VisibleTodoList listId="1" />
<VisibleTodoList listId="1" />
</div>
)
the selector gets called and result gets memoized
the selector gets called a second time, and it sees that { listId: 1 }
is the same prop arguments as the first time, so it just returns the memoized value.
If we render the component twice with different props:
return (
<div>
<VisibleTodoList listId="1" />
<VisibleTodoList listId="2" />
</div>
)
the selector gets called and the result gets memoized
the selector gets called a second time, and it sees that { listId: 2 }
is not the same props args as the first time { listId: 1 }
, so it recalculates and memoizes the new result (todo list 2 object) in memory (overwriting the previous memoization).
If we want each component to get its own memoization, each component instance must have its own selector instance.
For example:
// selector
const makeGetVisibleTodos = () => createSelector(
// ... get the visible todos
);
// each has their own memoization space:
const foo = makeGetVisibleTodos(); // this is an instance
const bar = makeGetVisibleTodos(); // this is a separate instance
So applying it to the component:
// component
const mapStateToProps = () => {
const getVisibleTodos = makeGetVisibleTodos(); // this instance get bound to the component instance
return (state, props) => {
return {
todos: getVisibleTodos(state, props)
}
}
}
Now, If we render the component twice with different props:
return (
<div>
<VisibleTodoList listId="1" />
<VisibleTodoList listId="2" />
</div>
)
with <VisibleTodoList listId="1" />
the first instance of the selector gets called and the result gets memoized
with <VisibleTodoList listId="2" />
a different instance of the selector gets called and the result gets memoized
No, it does not return wrong value. documentation just says memoization will not work at all for that case. To make it work(in meaning "save some resources and avoid repeating the same calculation") you need.
Actually docs says(last sentence in section you have quoted):
We’ll see how to overcome this limitation in the next section.
And next section Sharing Selectors with Props Across Multiple Component Instances says
To share a selector across multiple VisibleTodoList instances while passing in props and retaining memoization, each instance of the component needs its own private copy of the selector.
Also for sure you may increase memoization size to be more than 1.
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