web-dev-qa-db-fra.com

Comment utiliser Immutable.js avec redux?

Redux le cadre utilise réducteurs pour changer l'état de l'application en réponse à une action.

La condition essentielle est qu'un réducteur ne puisse pas modifier un objet d'état existant. il doit produire un nouvel objet.

Mauvais exemple :

import {
    ACTIVATE_LOCATION
} from './actions';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state.activeLocationId = action.id;
            break;
    }

    return state;
};

Bon exemple :

import {
    ACTIVATE_LOCATION
} from './actions';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = Object.assign({}, state, {
                activeLocationId: action.id
            });
            break;
    }

    return state;
};

C’est un bon cas d’utilisation pour Immutable.js .

50
Gajus

Prendre votre bon exemple avec Immutable

import {
    ACTIVATE_LOCATION
} from './actions';

import { Map } from 'immutable';

const initialState = Map({})

export let ui = (state = initialState, action) => {
  switch (action.type) {
    case ACTIVATE_LOCATION:
      return state.set('activeLocationId', action.id);
    default:
      return state;
  }
};
48
Geoffrey Abdallah

Immutable.js doit être utilisé dans chaque réducteur selon les besoins, par exemple.

import {
    ACTIVATE_LOCATION
} from './actions';

import Immutable from 'immutable';

export let ui = (state, action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = Immutable
                .fromJS(state)
                .set('activeLocationId', action.id)
                .toJS();
            break;
    }

    return state;
};

Cependant, cet exemple entraîne beaucoup de surcharge: chaque fois qu'une action de réduction est appelée, elle doit convertir un objet JavaScript en une instance de Immutable, transformer l'objet résultant en objet et le reconvertir en objet JavaScript.

Une meilleure approche consiste à faire de l’état initial une instance de Immutable:

import {
    ACTIVATE_LOCATION
} from './actions';

import Immutable from 'immutable';

let initialState = Immutable.Map([]);

export let ui = (state = initialState, action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = state.set('activeLocationId', action.id);
            break;
    }

    return state;
};

De cette façon, il vous suffit de convertir l'état initial en une instance de Immutable once. Ensuite, chaque réducteur le traitera comme une instance de Immutable et le transmettra à la ligne comme une instance de Immutable. Le problème, c'est que vous devez maintenant convertir l'état entier en JavaScript avant de transmettre les valeurs au contexte de la vue.

Si vous effectuez des mutations d’états multiples dans un réducteur, vous pouvez envisager mutations par lots en utilisant .withMutations.

Pour simplifier les choses, j'ai développé une bibliothèque redux-immutable . Il fournit la fonction combineReducers qui est équivalente à la fonction du même nom dans le paquetage redux, sauf qu'il attend l'état initial et que tous les réducteurs fonctionnent avec l'objet Immutable.js .

25
Gajus

Si vous cherchez simplement un moyen simple d’effectuer des mises à jour sans mutation, je tiens à jour une bibliothèque: https://github.com/substantial/updeep qui, à mon avis, est un bon moyen de faites cela avec redux.

updeep vous permet de travailler avec des hiérarchies d’objets (figées) régulières, de sorte que vous puissiez effectuer une déstructuration, voir les objets dans les journaux et le débogueur, etc. Il dispose également d’une API puissante permettant la mise à jour par lots. Cela ne sera pas aussi efficace que Immutable.js pour les grands ensembles de données, car il clone les objets s'il le faut.

Voici un exemple (mais vérifiez ceux dans le README pour plus)):

import {
    ACTIVATE_LOCATION
} from './actions';
import u from 'updeep';

export let ui = (state = [], action) => {
    switch (action.type) {
        case ACTIVATE_LOCATION:
            state = u({ activeLocation: action.id }, state);
            break;
    }

    return state;
};
8
Aaron Jensen

La réponse acceptée ne doit pas être la réponse acceptée. Vous devez initialiser l'état en utilisant immuable, puis (comme mentionné précédemment) en utilisant redux-immutablejs

const initialState = Immutable.fromJS({}) // or Immutable.Map({})

const store = _createStore(reducers, initialState, compose(
    applyMiddleware(...middleware),
    window.devToolsExtension ? window.devToolsExtension() : f => f
));

Que d'utiliser les combinés réducteurs de redux-immutablejs

Astuce supplémentaire: utiliser react-router-redux fonctionne plutôt bien, donc si vous souhaitez ajouter ceci, remplacez le réducteur de react-router-redux par ceci:

import Immutable from 'immutable';
import {
    UPDATE_LOCATION
} from 'react-router-redux';

let initialState;

initialState = Immutable.fromJS({
    location: undefined
});

export default (state = initialState, action) => {
    if (action.type === UPDATE_LOCATION) {
        return state.merge({
            location: action.payload
        });
    }

    return state;
};

Importez simplement ceci dans votre réducteur de racine

Ceci est également indiqué dans la documentation de redux-immutablejs

5
Michiel

Jetez un oeil à https://github.com/indexiatech/redux-immutablejs

C'est à peu près un combineReducer et un optionnel createReducer conforme aux standards Redux.

2
asaf000