使用拆分减速器更新相关状态字段的最佳方法?

Best way to update related state fields with split reducers?

我正在尝试找出理想的方法来更新我的状态树上的几个顶级字段,同时仍然保持拆分减速器。

这是我想出的一个简单的解决方案。

var state = {
  fileOrder: [0],
  files: {
    0:{
      id: 0,
      name: 'asdf'
    }
  }
};

function handleAddFile(state, action) {
  return {...state, ...{[action.id]:{id: action.id, name: action.name}}};
};

function addFileOrder(state, action) {
  return [...state, action.id];
}

// Adding a file should create a new file, and add its id to the fileOrder array.
function addFile(state, action) {
  let id = Math.max.apply(this, Object.keys(state.files)) + 1;
  return {
    ...state,
    fileOrder: addFileOrder(state.fileOrder, {id}),
    files: handleAddFile(state.files, {id, name: action.name})
  };
}

目前我可以发送一个动作 {type: ADD_FILE, fileName: 'x'},然后 addFile 在内部创建一个动作发送到 addFileOrderaddFile.

我很好奇它是否被认为是执行以下任一操作的更好方法。

而是分派两个动作,一个是添加文件,然后获取它的 ID 并分派带有该 ID 的 ADD_TO_FILE_ORDER 动作。 或者像 {type: ADD_FILE, name: 'x', id: 1} 那样触发和操作,而不是让 addFile 计算新的 id。这将允许我使用 combineReducers 并根据操作类型进行过滤。 这个例子可能很简单,但我的实际状态树有点复杂,每个添加的文件也需要添加到其他实体。

对于一些额外的上下文,更完整的状态树看起来像这样。

{
    "fileOrder": [0]
    "entities": {
        "files": {
            0: {
                id: 0,
                name: 'hand.png'
            }
        },
        "animations": {
            0: {
                id: 0,
                name: "Base",
                frames: [0]
            }
        },
        "frames": {
            0: {
                id: 0,
                duration: 500,
                fileFrames: [0]
            }
        },
        "fileFrames": {
            0: {
                id: 0,
                file: 0,
                top: 0,
                left: 0,
                visible: true
            }           
        }
    }
}

添加文件需要:

  1. 将其添加到文件哈希。
  2. 将其添加到 fileOrder 数组。
  3. 为每个帧添加引用文件的 fileFrame。
  4. 将每个新的 fileFrame 添加到为其创建的框架中。

最后两点让我想知道我是否能够使用 combineReducers。

我最终找到了解决这个问题的非常简单的方法。

文档中的这两个块在功能上是相同的。

const reducer = combineReducers({
  a: doSomethingWithA,
  b: processB,
  c: c
});

// This is functionally equivalent.
function reducer(state, action) {
  return {
    a: doSomethingWithA(state.a, action),
    b: processB(state.b, action),
    c: c(state.c, action)
  };
}

我最终调整了第二个块,并始终沿着我的全局状态树传递。只要一路上没有任何东西编辑状态,所有的减速器都可以正常工作。

// Simple change to pass the entire state to each reducer.
// You have to be extra careful to keep state immutable here.
function reducer(state, action) {
  return {
    a: doSomethingWithA(state.a, action, state),
    b: processB(state.b, action, state),
    c: c(state.c, action, state)
  };
}

基于作者的解决方案:

我一直遇到同样的问题,我需要(只是一点点)访问我的 reducer 状态之外的部分。我认为如果您努力不更改除标志或计数器等单个值以外的任何其他内容,那么此解决方案可以在实践中发挥作用。

如果其他开发人员对他们的代码不那么保留,那么杂质可能会很快变得疯狂。想象一下,如果 a 开始改变 b 和 c 的状态部分,b 改变 a 和 c 的部分,等等,会发生什么。

您可以考虑像这样缩小杂质的表面积:

function reducer(state, action) {
  return {
    a: doSomethingWithA(state.a, action, state.globals),
    b: processB(state.b, action, state.globals),
    c: c(state.c, action, state.globals)
  };
}