如何在 redux 中使用 Immutable.js?

How to use Immutable.js with redux?

Redux framework is using reducers 更改应用程序状态以响应操作。

关键要求是reducer不能修改现有的状态对象;它必须产生一个新对象。

坏例子:

import {
    ACTIVATE_LOCATION
} from './actions';

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

    return state;
};

好例子:

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

这是 Immutable.js 的一个很好的用例。

Immutable.js 应该根据需要在每个 reducer 中使用,例如

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

然而,这个例子中有很多开销:每次调用 reducer 动作时,它必须将 JavaScript 对象转换为 Immutable 的实例,改变结果对象并将其转换回JavaScript 对象。

更好的方法是让初始状态成为 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;
};

这样,您只需要将初始状态转换为Immutable盎司的实例。然后每个 reducer 会将其视为 Immutable 的实例,并将其作为 Immutable 的实例传递给行。问题是现在您需要在将值传递给视图上下文之前将整个状态转换为 JavaScript。

如果您在减速器中执行多个状态突变,您可能需要考虑 batching mutations 使用 .withMutations

为了让事情更简单,我开发了一个 redux-immutable library. It provides combineReducers function thats equivalent to the function of the same name in the redux package, except that it expect the initial state and all reducers to work with Immutable.js 对象。

如果您只是在寻找一种简单的方法来进行无突变更新,我维护了一个库:https://github.com/substantial/updeep,在我看来,这是使用 redux 进行更新的好方法.

updeep 允许您使用常规(冻结的)对象层次结构,因此您可以进行解构、在日志和调试器中查看对象等。它还有一个强大的 API,允许批量更新。对于大型数据集,它不会像 Immutable.js 那样高效,因为它会在需要时克隆对象。

这是一个示例(但请查看 README 中的示例以获取更多信息):

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

以 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;
  }
};

看看https://github.com/indexiatech/redux-immutablejs

它几乎是一个 combineReducer 和一个符合 Redux 标准的可选 createReducer

接受的答案不应该是接受的答案。 您需要使用 immutable 初始化状态,然后(如前所述)使用 redux-immutablejs

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

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

比使用 redux-immutablejs 的 combineReducers

额外提示: 使用 react-router-redux 效果很好所以如果你想添加这个然后用这个替换 react-router-redux 的减速器:

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

只需将其导入您的根减速器

这个在redux-immutablejs的文档中也有说明