何时使用.toJS()与Immutable.js和Flux?
我正在尝试使用ImmutableJS和我的React / Flux应用程序。
我的商店是Immutable.Map
对象。
我想知道我应该在哪一点使用.toJS()
? 应该是当商店的.get(id)
返回? 或与.get('member')
?
理想的是,永远不要!
如果您的Flux商店正在使用Immutable.js,那么请尽量保持完整。 使用React.addons.ReactComponentWithPureRenderMixin实现memoization性能赢(它增加了一个shouldComponentUpdate方法)。
渲染时,可能需要调用toJS()
因为React v0.12.x只接受Array
作为子对象:
render: function () { return ( <div> {this.props.myImmutable.map(function (item) { <div>{item.title}</div> }).toJS()} </div> ); }
这在React v0.13.x中已经改变了。 组件接受任何Iterable儿童,而不是只有Array
。 由于Immutable.js实现了Iterable,因此可以省略toJS()
:
render: function () { return ( <div> {this.props.myImmutable.map(function (item) { <div>{item.title}</div> })} </div> ); }
有点古老的问题,但最近我正在试验这种方法,使用重新select和lodash的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,它将被映射到2个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; } // ... }
这样,如果todos商店没有任何改变,当我调用getTodos()
重新select返回给我同一个对象,所以没有得到重新呈现。
例如,如果将id 2 si标记为已完成的待办事项,它也会在商店中更改,因此新的对象将由todosSelector
返回。 然后, memoizeTodo
通过memoizeTodo
函数进行映射,如果待办事项不变(因为它们是不可变的地图),应该返回相同的对象。 所以当TodoList
收到新的道具时,它会重新渲染,因为待办事项已经改变,但是只有第二个TodoItem
重新渲染,因为代表待办事项ID为1的对象没有改变。
这肯定会导致性能损失,特别是如果我们的商店包含很多项目,但我没有注意到我的中型应用程序中有任何问题。 这种方法的好处是你的组件接收纯JavaScript对象作为道具,并可以像PureRenderMixin
一样使用它们,所以如何从商店返回对象不再是组件的业务。
希望这是有道理的,我的英语是非常糟糕的:/
就像@ LeeByron所说的,你不应该叫一个toJS
。 在React 0.14。*下,在不可变map
上调用map
将正常工作并呈现,但是最终会显示警告:
使用地图作为孩子还没有完全支持。 这是一个可能被删除的实验性function。 将其转换为键控ReactElements的序列/迭代。
为了解决这个问题,你可以在你的Map
上调用toArray()
,如下所示:
render () { return ( <div> {this.props.immutableMap.toArray().map(item => { <div>{item.title}</div> })} </div> ) }
将你的迭代转换成一个数组,并给予React它想要的。
@Hummlas提出的好点。
我个人在我的React组件中使用它,当我遍历一个集合来呈现一个子组件的数组:
this.props.myImmutableCollection.map(function(item, index) { React.DOM.div null, item.get('title'); }).toJS();
如果您不使用.toJS(),则React将不会将映射元素识别为组件数组。
– 不再推荐 –
当使用Redux时,我倾向于让连接的mapStateToProps函数使用toJS()来转换不可变的结构,并允许我的反应组件使用道具作为javascript对象。