When to use .toJS() with Immutable.js and Flux?

后端 未结 5 622
礼貌的吻别
礼貌的吻别 2021-01-30 16:38

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

相关标签:
5条回答
  • 2021-01-30 17:09

    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.

    0 讨论(0)
  • 2021-01-30 17:09

    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.

    0 讨论(0)
  • 2021-01-30 17:22

    -- 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.

    0 讨论(0)
  • 2021-01-30 17:26

    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.

    0 讨论(0)
  • 2021-01-30 17:27

    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>
      );
    }
    
    0 讨论(0)
提交回复
热议问题