有点老问题,但最近我一直在尝试使用 reselect 和 lodash's memoize 来尝试将可比较的对象返回给 React 的组件。
想象一下你有这样一家商店:
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)
);
然后我传递给一个TodoList组件todos作为prop,然后将其映射成两个TodoItem组件:
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;
}
// ...
}
这样,如果待办事项的存储中没有任何变化,当我调用 getTodos() 时,reselect 会返回给我相同的对象,因此不会重新渲染任何内容。
例如,如果 ID 为 2 的待办事项被标记为已完成,它也会在存储中更改,因此 todosSelector 返回一个新对象。然后通过memoizeTodo 函数映射待办事项,如果待办事项没有更改(因为它们是不可变映射),它应该返回相同的对象。因此,当TodoList 收到新的道具时,它会重新渲染,因为todos 已更改,但只有第二个TodoItem 重新渲染,因为表示 id 为 1 的 todo 的对象没有更改。
这肯定会导致性能损失,特别是如果我们的商店包含很多商品,但我没有注意到我的中型应用程序有任何问题。这种方法的好处是您的组件接收纯 javascript 对象作为道具,并且可以将它们与PureRenderMixin 之类的东西一起使用,因此如何从商店返回对象不再是组件的业务。