更新/更短的方式来更新Redux中的嵌套状态?

ffx*_*sam 29 javascript reactjs redux

有时减速器会变得混乱:

const initialState = {
    notificationBar: {
        open: false,
    },
};

export default function (state = initialState, action) {
  switch (action.type) {
    case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
      return Object.assign({}, state, {
        // TODO: Find a cleaner way to do this!
        notificationBar: Object.assign({}, state.notificationBar, {
          open: true,
        }),
      });
    default:
      return state;
  }
}
Run Code Online (Sandbox Code Playgroud)

是否有更简洁的方法来做到这一点?

Dan*_*nce 46

虽然可以使用扩展运算符,但是有很多其他方法可以实现相同的结果,甚至不需要将来的JS编译器用于非标准化功能.以下是一些其他选项,没有特别的顺序.

返回一个文字

如果您确定您的状态不会增长,那么您可以简单地将整个新状态作为文字返回.

return {
  notificationBar: {
    open: true
  }
}
Run Code Online (Sandbox Code Playgroud)

然而,这通常不合适,因为你的州不太可能这么简单.


结合减速机

Redux为您提供了一种实用方法,用于组合在状态对象的不同部分上工作的多个Reducer.在这种情况下,您将创建一个notificationBar仅处理此对象的reducer.

 createStore(combineReducers({
   notificationBar: function(state=initialNBarState, action) {
     switch (action.type) {
       case actions.LAYOUT_NOTIFICATIONBAR_OPEN:
         return Object.assign({}, state, { open: true });
   }
 });
Run Code Online (Sandbox Code Playgroud)

这可以防止您不必担心顶级属性,这样就可以避免嵌套调用Object.assign.

如果您的状态可以在逻辑上划分为明确定义的部分,那么这可能是解决此问题的最惯用的方法.


使用不可变数据

您可以使用持久性数据结构库来创建数据结构,而不是可以修改以返回副本.

Mori是将Clojure的数据结构和功能API编译成JS的结果.

import { hashMap, updateIn } from 'mori';

const initialState = hashMap(
  "notificationBar", hashMap(
    "open", false
  )
);

// ...

return updateIn(state, ['notificationBar', 'open'], true);
Run Code Online (Sandbox Code Playgroud)

ImmutableJS

ImmutableJS是将Hash Array Mapped Tries的语义从Clojure的持久数据结构带入Javascript的更为迫切的方法.

import { Map } from 'immutable';

const initialState = Map({
  notificationBar: Map({
    open: true
  });
});

// ...

return state.setIn(['notificationBar', 'open'], true);
Run Code Online (Sandbox Code Playgroud)

别号 Object.assign

您可以创建更友好的版本Object.assign来编写上面代码的高级版本.事实上,它几乎与...运营商一样简洁.

function $set(...objects) {
  return Object.assign({}, ...objects);
}

return $set(state, {
  notificationBar: $set(state.notificationBar, {
    open: true,
  })
});
Run Code Online (Sandbox Code Playgroud)

使用不可变助手

有许多库也提供不变性助手,用于修改常规可变对象.

反应-插件更新

React长期拥有一套内置的不变性助手.它们使用与MongoDB查询类似的语法.

import update from 'react-addons-update';

return update(state, {
  notificationBar: {
    open: { $set: true }
  }
});
Run Code Online (Sandbox Code Playgroud)

点丙不变

此库允许您使用熟悉的点路径指定(嵌套)属性的更新.

import dotProp from 'dot-prop-immutable';

return dotProp.set(state, 'notificationBar.open', true);
Run Code Online (Sandbox Code Playgroud)

更新式

该库是一个包装器react-addons-update,为更新(嵌套)属性提供了更多功能的语法.

您传递的函数不是传递新值,而是传递旧值并返回一个新值.

import updateIn from 'update-in';

return updateIn(state, ['notificationBar', 'open'], () => true);
Run Code Online (Sandbox Code Playgroud)

一成不变的路径

对于更新属性,此库就像是dot-prop-immutable和之间的交叉update-in.

import path from 'immutable-path';

return path.map(state, 'notificationBar.open', () => true);
Run Code Online (Sandbox Code Playgroud)

  • 以及类似于React Helpers的各种其他库,例如dot-prop-immutable,update-in,immutable-path和object-path-immutable. (2认同)
  • @ffxsam它适用于数组,不适用于对象. (2认同)

zer*_*kms 45

UPD:它现在是ES2018的一部分

它可能会通过非标准化但属性扩展语法略微改进:

return {
    ...state,
    notificationBar: {
        ...state.notificationBar,
        open: true,
    },
};
Run Code Online (Sandbox Code Playgroud)


Saf*_*eli 6

你可以使用镜头.

import { set, makeLenses } from '@DrBoolean/lenses'

const L = makeLenses(['notificationBar', 'open']);
const notificationBarOpen = compose(L.notificationBar, L.open)
const setNotificationBarOpenTrue = set(notificationBarOpen, true)

const a = { notificationBar: { open: false } }
const b = setNotificationBarOpenTrue(a) 
// `a` is left unchanged and `b` is `{ notificationBar: { open: true } }`
Run Code Online (Sandbox Code Playgroud)

您可以将镜头视为组合属性访问/更新.

关于镜片的一些好资源:

如果您对阅读lisps感到满意,我还建议您从球拍文档中了解镜片的这一优秀介绍.最后,如果你想更深入,并且可以阅读haskell,你可以观看:镜头 - 组合数据访问和操作.