Лучший способ обновить связанные поля состояния с помощью 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 создает действие внутри для отправки в addFileOrder и addFile.

мне любопытно, считается ли это лучшим подходом для выполнения любого из приведенных ниже.

вместо отправить два действия, один, чтобы добавить файл, а затем получить его идентификатор и отправить ADD_TO_FILE_ORDER действие с идентификатором. Или огонь и действие, как {type: ADD_FILE, name: 'x', id: 1}, вместо addFile для вычисления нового идентификатора. Это позволит мне использовать 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. добавьте каждый новый файловый фрейм в фрейм, для которого он был создан.

последние два момента заставляют меня задуматься, смогу ли я вообще использовать combineReducers.

2 ответов


Я нашел довольно простое решение этой проблемы.

оба этих блока из документации функционально одинаковы.

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

опираясь на авторское решение:

У меня была такая же проблема, где мне нужен (немного) доступ за пределами части моего редуктора государства. Я думаю, что это решение может работать на практике, если вы старательно не меняете ничего, кроме одного значения, такого как флаг или счетчик.

это нечистота может быстро сойти с ума, если другие разработчики не были так защищены своим кодом. Представьте, что произойдет, если 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)
  };
}