Best way to update related state fields with split reducers?

╄→гoц情女王★ 提交于 2019-12-08 18:24:45

问题


I'm trying to work out the ideal way to update several top level fields on my state tree while still maintaining split reducers.

Here's a simple solution that I've come up with.

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

Currently I'm able to dispatch a single action {type: ADD_FILE, fileName: 'x'}, then addFile creates an action internally to send to addFileOrder and addFile.

I'm curious if it is considered a better approach to do either of the below.

Instead dispatch two actions, one to add a file, then get it's id and dispatch an ADD_TO_FILE_ORDER action with the id. OR Fire and action like {type: ADD_FILE, name: 'x', id: 1}, instead of allowing addFile to calculate the new id. This would allow me to use combineReducers and filter on action type. This example is probably trivial, but my actual state tree is a bit more complicated, with each file being added also needing to be added to other entities.

For some additional context, a more complete state tree would look like this.

{
    "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
            }           
        }
    }
}

Adding a file would need to:

  1. Add it to the files hash.
  2. Add it to the fileOrder array.
  3. Add a fileFrame referencing the file, for each of the frames.
  4. Add each new fileFrame to the frame that it was created for.

The last two points make me wonder if I'd be able to use combineReducers at all.


回答1:


I ended up finding a pretty simple solution to this problem.

Both of these blocks from the documentation are functionally the same thing.

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

I ended up tweaking the second block, and always passing along my global state tree. As long as nothing edits the state along the way, all the reducers work fine.

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



回答2:


Building on the author's solution:

I've been having this same issue, where I need (just a little) access outside of my reducer's part of of the state. I think this solution can work in practice if you're diligent about not changing anything other than a single value like a flag, or a counter.

It's impurity could get crazy fast if other developers weren't as reserved with their code. Imagine what would happen if a started changing b and c's part of the state, b changing a and c's part, and so on.

You might consider shrinking the surface area of impurity like this:

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


来源:https://stackoverflow.com/questions/33321397/best-way-to-update-related-state-fields-with-split-reducers

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!