web-dev-qa-db-fra.com

Quand utiliser .toJS () avec Immutable.js et Flux?

J'essaie d'utiliser ImmutableJS avec mon React/application Flux.

Mes magasins sont des objets Immutable.Map.

Je me demande à quel moment dois-je utiliser .toJS()? Cela devrait-il se produire lorsque la fonction .get(id) du magasin revient? ou dans les composants avec .get('member')?

41
chollier

Idéalement, jamais!

Si vos magasins Flux utilisent Immutable.js, essayez de continuer tout au long du processus. Utilisez React.addons.ReactComponentWithPureRenderMixin pour obtenir un gain de performances de mémorisation (il ajoute une méthode shouldComponentUpdate).

Lors du rendu, vous devrez peut-être appeler toJS() as React v0.12.x accepte uniquement Array comme enfants:

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

Cela a changé dans React v0.13.x. Les composants acceptent tout Iterable comme enfant au lieu de seulement Array. Comme Immutable.js implémente Iterable, vous pouvez omettre le toJS():

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

Une vieille question, mais récemment, j'ai expérimenté cette approche en utilisant resélectionner et mémo de lodash dans le but de retourner des objets comparables aux composants de React.

Imaginez que vous ayez un magasin comme celui-ci:

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)
);

Ensuite, je passe à un composant TodoListtodos comme accessoire, qui sera ensuite mappé en deux composants 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;
    }

    // ...
}

De cette façon, si rien n'a changé dans le magasin de tâches, lorsque j'appelle getTodos() reselect me renvoie le même objet, et donc rien n'est restitué.

Si, par exemple, la tâche à effectuer avec l'ID 2 Est marquée comme terminée, elle change également dans le magasin et un nouvel objet est donc renvoyé par todosSelector. Ensuite, les tâches sont mappées par la fonction memoizeTodo, qui devrait retourner le même objet si une tâche n'est pas modifiée (car ce sont des cartes immuables). Ainsi, lorsque TodoList reçoit les nouveaux accessoires, il effectue un nouveau rendu parce que todos a changé, mais seul le second TodoItem est à nouveau rendu car l'objet représentant la tâche avec l'ID 1 n'a pas ne change pas.

Cela pourrait sûrement entraîner une perte de performances, surtout si notre magasin contient beaucoup d'articles, mais je n'ai remarqué aucun problème dans mon application de taille moyenne. L'avantage de cette approche est que vos composants reçoivent des objets javascript simples en tant qu'accessoires et pourraient les utiliser avec quelque chose comme PureRenderMixin, donc la façon dont les objets sont renvoyés du magasin n'est plus l'affaire des composants.

4
Ingro

Comme l'a dit @LeeByron, vous ne devriez pas avoir à appeler un toJS. Sous React 0.14. *, L'appel de map sur un Map immuable fonctionnera et restituera correctement, mais vous vous retrouverez avec un avertissement:

L'utilisation de Maps en tant qu'enfants n'est pas encore entièrement prise en charge. Il s'agit d'une fonctionnalité expérimentale qui pourrait être supprimée. Convertissez-le à la place en une séquence/itérable de ReactElements à clé.

Pour y faire face, vous pouvez appeler toArray() sur votre Map comme:

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

Convertir votre itérable en tableau et donner React ce qu'il veut.

3
Balthazar

Bon point soulevé par @Hummlas.

Je l'utilise personnellement dans mes composants React, lorsque je parcourt une collection pour rendre un tableau de sous-composants:

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

Si vous n'utilisez pas .toJS (), React ne reconnaîtra pas les éléments mappés comme un tableau de composants.

2
Joseph

- Ne recommande plus -
.

0
walkerrandophsmith