2015-01-09 30 views
37

我想用我的React/Flux应用程序使用ImmutableJS。何时使用.toJS()与Immutable.js和Flux?

我的店是Immutable.Map对象。

我不知道在这一点,我应该使用.toJS()?商店的.get(id)是否应该返回?或在与.get('member')的组件?通过@Hummlas提出

+1

好问题。我不会在商店中做到这一点,因为如果你想用shouldComponentUpdate优化渲染,那么你就失去了进行简单对象比较(prevState!== this.state)的能力。 –

+0

非常感谢,不要在商店中使用'toJS()'。 – chollier

回答

1

好一点。

我personnally用它在我的反应的组分,当我遍历一个集合,以使子组件阵列:

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

如果你不使用.toJS(),阵营将无法识别映射元素作为组件数组。

42

理想的情况下,从来没有!

如果你的流量商店使用Immutable.js然后试图通过维持所有的方式。使用React.addons.ReactComponentWithPureRenderMixin实现memoization性能赢(它增加了一个shouldComponentUpdate方法)。

渲染时,您可能需要调用toJS()为阵营v0.12.x只接受Array儿童:

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

这已经改变作出反应v0.13.x.组件接受任何Iterable作为孩子,而不仅仅是Array。由于Immutable.js实现可迭代,你可以省略toJS()

render: function() { 
    return (
    <div> 
     {this.props.myImmutable.map(function (item) { 
     <div>{item.title}</div> 
     })} 
    </div> 
); 
} 
+4

李,你对proptypes有什么建议吗?我习惯使用'arrayOf(shape ...)'。 – Zach

+0

现在如果只有我的外部库也支持Immutable.js – pherris

+4

那么,我们是否说在React组件中使用来自Immutable.js的读取API('get()','getIn()')是正确的做法?这感觉就像是抽象的抽象概念,意味着如果我改变了我在商店中维护状态的方式,就必须触及每个读取值的组件。有些东西对此感觉不对... – sethro

0

- 不再推荐 -
当使用终极版我倾向于让我的连接mapStateToProps函数变换使用toJS不变的结构()并允许我的反应组件使用道具作为JavaScript对象。

+3

但是,toJS将返回一个新的深度克隆对象。这不利于表演。 –

+0

这是真的,但是使用类似重新选择的东西,只有当底层的不可变对象发生了变化时才能返回新对象。唯一的问题是嵌套数据。例如,如果地图列表中只有一个元素发生了变化,则会返回一个包含新对象的全新数组,并在每个子视图中触发一个渲染。随着不可改变,只有变化后的孩子才会重新呈现...... – Ingro

+3

因为表现,我已经停止了我所建议的。 – walkerrandophsmith

3

有点古老的问题,但最近我正在试验这种方法,使用reselectlodash'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作为支柱,其将在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()重新选择返回给我同一个对象,所以没有重新呈现。

如果例如待办事项ID为2 SI标记为完成,它也改变了存储,所以一个新的对象被todosSelector返回。然后,待办事项由memoizeTodo函数映射,如果待办事项未更改(因为它们是不可变的地图),该函数应返回相同的对象。所以当TodoList收到新的道具时,它会重新渲染,因为todos已更改,但只有第二个TodoItem重新渲染,因为表示id为1的待办事项的对象未更改。

这肯定会导致性能损失,特别是如果我们的商店包含很多项目,但我没有发现我的中型应用程序有任何问题。这种方法的好处是你的组件接收纯JavaScript对象作为道具,并可以像PureRenderMixin一样使用它们,所以从商店返回对象的方式不再是组件的业务。

希望这是有道理的,我的英语非常糟糕:/

+0

惊人的解决方案! –

3

像@LeeByron说,你不应该叫toJS。下反应0.14 *,调用一个不变的Mapmap将工作和正确渲染,但你会最终了一个警告:

使用地图作为孩子还没有完全支持。这是一个可能会被删除的实验性功能。相反,将其转换为键控ReactElements的序列/迭代。

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

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

转换您可迭代到一个数组中,并给予阵营想要的东西。

+0

谢谢你的回答一直在寻找这么久! – user2861799

+0

@ user2861799不客气,很高兴帮助:) –

相关问题