I'm trying to use ImmutableJS with my React / Flux application.
My stores are Immutable.Map
objects.
I'm wondering at which point should I use .toJS()
? Should it be when the store's .get(id)
returns ? or in the components with .get('member')
?
Ideally, never!
If your Flux stores are using Immutable.js then try to maintain all the way through. Use React.addons.ReactComponentWithPureRenderMixin to achieve a memoization performance win (it adds a shouldComponentUpdate methods).
When rendering, you may need to call toJS()
as React v0.12.x only accepts Array
as children:
render: function () {
return (
<div>
{this.props.myImmutable.map(function (item) {
<div>{item.title}</div>
}).toJS()}
</div>
);
}
This have changed in React v0.13.x. Components accept any Iterable as children instead of only Array
. Since Immutable.js implements Iterable, you are able to omit the toJS()
:
render: function () {
return (
<div>
{this.props.myImmutable.map(function (item) {
<div>{item.title}</div>
})}
</div>
);
}
Kinda old question but lately I've been experimenting with this approach using reselect and lodash's memoize in the effort of returning comparable objects to React's Components.
Imagine you have a store like this:
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)
);
Then I pass to a TodoList
component todos
as a prop, which will then be mapped into two TodoItem
Components:
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;
}
// ...
}
This way, if nothing changed in the todo's store, when I call getTodos()
reselect returns to me the same object, and so nothing gets re-rendered.
If, for example, todo with id 2
is marked as completed, it also changes in the store, and so a new object is returned by todosSelector
. Then the todos are mapped by memoizeTodo
function, which should return the same object if a todo isn't changed (since they are Immutable Maps). So when TodoList
receives the new props, it re-renders because todos
has changed, but only the second TodoItem
re-renders because the object representing the todo with id 1 didn't change.
This surely could lead to a performance loss, especially if our store contains a lot of items, but I didn't notice any problem in my medium-size app. The upside of this approach is that your Components receives plain javascript objects as props and could use them with something like PureRenderMixin
, so how objects are returned from the store is not business of Components anymore.
Like @LeeByron said, you shouldn't have to call a toJS
. Under React 0.14.*, calling map
on an Immutable Map
will work and render correctly, but you will end-up with a warning:
Using Maps as children is not yet fully supported. It is an experimental feature that might be removed. Convert it to a sequence / iterable of keyed ReactElements instead.
To deal with this, you can call toArray()
on your Map
like:
render () {
return (
<div>
{this.props.immutableMap.toArray().map(item => {
<div>{item.title}</div>
})}
</div>
)
}
Converting your iterable to an array and giving React what it wants.
Good point raised by @Hummlas.
I personnally use it in my React components, when I iterate over a collections to render an array of sub components:
this.props.myImmutableCollection.map(function(item, index) {
React.DOM.div null, item.get('title');
}).toJS();
If you don't use .toJS(), React won't recognize the mapped elements as an array of components.
-- No longer recommend --
When using Redux I tend to let my connects' mapStateToProps function transform immutable structures using toJS() and allow my react components to consume the props as javascript objects.
来源:https://stackoverflow.com/questions/27864706/when-to-use-tojs-with-immutable-js-and-flux