何时使用带有Immutable.js和Flux的.toJS()?

cho*_*ier 42 reactjs reactjs-flux immutable.js

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

我的商店是Immutable.Map物品.

我想知道我应该在哪个方面使用.toJS()?应该是商店的.get(id)回归吗?或在组件中.get('member')

Lee*_*ron 44

理想情况下,永远!

如果您的Flux商店使用的是Immutable.js,那么请尽量保持完整.使用React.addons.ReactComponentWithPureRenderMixin实现memoization performance win(它添加了一个shouldComponentUpdate方法).

渲染时,您可能需要调用,toJS()因为React v0.12.x只接受Array作为子项:

render: function () {
  return (
    <div>
      {this.props.myImmutable.map(function (item) {
        <div>{item.title}</div>
      }).toJS()}
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

这已在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>
  );
}
Run Code Online (Sandbox Code Playgroud)

  • 李,你对proptypes有什么建议吗?我习惯使用`arrayOf(shape ...)`. (4认同)
  • 那么,我们是说在React组件中使用来自Immutable.js的读取API(`get()`,`getIn()`)是正确的做法吗?这感觉就像是漏洞抽象,意味着如果我改变了我在商店中保持状态的方式,我必须触摸每个读取值的组件.这件事感觉不对...... (4认同)

Ing*_*gro 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)
);
Run Code Online (Sandbox Code Playgroud)

然后我将一个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;
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

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

例如,如果带有 id 的待办事项2被标记为已完成,它也会在存储中发生变化,因此一个新对象由 返回todosSelector。然后 todo 被memoizeTodo函数映射,如果 todo 没有改变,它应该返回相同的对象(因为它们是不可变的映射)。因此,当TodoList接收到新的 props 时,它会重新渲染,因为todos已更改,但只有第二次TodoItem重新渲染,因为表示 i​​d 为 1 的待办事项的对象没有更改。

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


小智 2

@Hummlas 提出的好点。

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

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

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