43

我正在尝试将 ImmutableJS 与我的 React / Flux 应用程序一起使用。

我的商店是Immutable.Map对象。

我想知道我应该在哪一点使用.toJS()?应该是商店.get(id)退货的时候吗?或在组件中.get('member')

4

5 回答 5

45

理想情况下,永远不会!

如果您的 Flux 商店正在使用 Immutable.js,那么请尝试一直维护。使用 React.addons.ReactComponentWithPureRenderMixin 来获得记忆性能的胜利(它添加了一个 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>
  );
}
于 2015-01-21T06:04:08.847 回答
5

有点老问题,但最近我一直在尝试这种方法,使用reselectlodash 的 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)
);

然后我作为 props 传递给一个TodoList组件todos,然后将其映射为两个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;
    }

    // ...
}

这样,如果 todo 的存储中没有任何变化,当我调用getTodos()reselect 时,会返回相同的对象,因此不会重新渲染。

例如,如果带有 id 的 todo2被标记为已完成,它也会在 store 中更改,因此由 . 返回一个新对象todosSelector。然后 todos 由memoizeTodo函数映射,如果 todo 没有更改(因为它们是不可变映射),它应该返回相同的对象。所以当TodoList接收到新的道具时,它会重新渲染,因为todos已经改变了,但只有第二个TodoItem重新渲染,因为表示 i​​d 为 1 的 todo 的对象没有改变。

这肯定会导致性能损失,特别是如果我们的商店包含很多商品,但我没有注意到我的中型应用程序有任何问题。这种方法的好处是您的组件接收普通的 javascript 对象作为道具,并且可以将它们与类似的东西一起使用PureRenderMixin,因此如何从商店返回对象不再是组件的业务。

于 2016-01-20T09:30:51.453 回答
3

就像@LeeByron 说的那样,你不应该打电话给toJS. 在 React 0.14.* 下,调用mapImmutableMap将正常工作并呈现,但您最终会收到警告:

尚不完全支持将地图用作子项。这是一个实验性功能,可能会被删除。将其转换为键控 ReactElements 的序列/可迭代。

为了解决这个问题,你可以打电话toArray()给你的Map喜欢:

render () {
  return (
    <div>
      {this.props.immutableMap.toArray().map(item => {
        <div>{item.title}</div>
      })}
    </div>
  )
}

将您的可迭代对象转换为数组并为 React 提供所需的内容。

于 2016-03-09T00:48:36.313 回答
2

@Hummlas 提出的好观点。

我个人在我的 React 组件中使用它,当我迭代集合以呈现子组件数组时:

this.props.myImmutableCollection.map(function(item, index) {
    React.DOM.div null, item.get('title');
}).toJS();

如果你不使用 .toJS(),React 不会将映射的元素识别为组件数组。

于 2015-01-12T16:26:49.177 回答
0

-- 不再推荐 --
使用 Redux 时,我倾向于让我的连接的 mapStateToProps 函数使用 toJS() 转换不可变结构,并允许我的反应组件将道具作为 javascript 对象使用。

于 2016-01-04T01:58:15.090 回答