2015-01-09 97 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不客氣,很高興幫助:) –

相關問題