Kinda old question but lately I've been experimenting with this approach using reselect and lodash's memoize in the effort of returning comparable objects to React's Components.
Imagine you have a store like this:
import { List, Map } from 'immutable';
import { createSelector } from 'reselect';
import _ from 'lodash';
const store = {
todos: List.of(
Map({ id: 1, text: 'wake up', completed: false }),
Map({ id: 2, text: 'breakfast', completed: false })
)
};
const todosSelector = state => state.todos;
function normalizeTodo(todo) {
// ... do someting with todo
return todo.toJS();
}
const memoizeTodo = _.memoize(normalizeTodo);
export const getTodos = createSelector(
todosSelector,
todos => todos.map(memoizeTodo)
);
Then I pass to a TodoList
component todos
as a prop, which will then be mapped into two TodoItem
Components:
class TodoList extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.todos !== nextProps.todos;
}
render() {
return (<div>
{this.props.todos.map(todo => <TodoItem key={todo.id} todo={todo} />)}
</div>);
}
}
class TodoItem extends React.Component {
shouldComponentUpdate(nextProps) {
return this.props.todo !== nextProps.todo;
}
// ...
}
This way, if nothing changed in the todo's store, when I call getTodos()
reselect returns to me the same object, and so nothing gets re-rendered.
If, for example, todo with id 2
is marked as completed, it also changes in the store, and so a new object is returned by todosSelector
. Then the todos are mapped by memoizeTodo
function, which should return the same object if a todo isn't changed (since they are Immutable Maps). So when TodoList
receives the new props, it re-renders because todos
has changed, but only the second TodoItem
re-renders because the object representing the todo with id 1 didn't change.
This surely could lead to a performance loss, especially if our store contains a lot of items, but I didn't notice any problem in my medium-size app. The upside of this approach is that your Components receives plain javascript objects as props and could use them with something like PureRenderMixin
, so how objects are returned from the store is not business of Components anymore.
toJS()
in the stores. – Candiecandied