【问题标题】:Modifying state for specific item in array in redux reducer在redux reducer中修改数组中特定项目的状态
【发布时间】:2020-04-16 08:33:14
【问题描述】:

我无法完全理解 的样板。我查找了immutable modifying of state 的常见模式,但问题是,所有这些模式都只是推到最后,而不是针对特定索引。

在我进入实际代码之前,为了更好地想象,这是状态结构的样子(伪代码)

state = {
   quizMenu: {...},
   quizEditor: Array<Question>,

    > type Question = {
        id: number,
        question: string,
        questionOptions: Array<QuestionOption>,
      }

        > type QuestionOption = {
           id: number,
           optionText: string,
           isValid: boolean,
          }
}

希望这是有道理的。我创建了一个添加问题的操作,效果很好。现在,我尝试创建一个操作以向现有问题添加选项,但我不知道如何在嵌套的对象数组中添加选项。

以下是我的相关操作的定义方式:

const AQO = 'ADD_QUESTION_OPTION';

/*
 * @param questionId - ID of the question we're accesssing in quizEditor array
 * @param id - id of the option we're adding (handled in component)
*/
const actionAddQuestionOption = createAction(
  AQO, 
  (questionId: number, id: number) => ({ 
    payload: {
      id,
      optionText: 'New option',
      isValid: false,
      questionId,
    },
  })
);

现在我的reducer是这样的:

const reducer = createReducer({//...}, {
   [actionAddQuestionOption.type]: (state, action) => ({
     ...state,
     quizEditor: [...state.quizEditor][action.payload.questionId].questionOptions.push({
        id: action.payload.id,
        optionText: action.payload.optionText,
        isValid: action.payload.isValid,
     })
   })
}

这只是以这个怪物类型错误结束:https://pastebin.com/raw/pBbnxcQp

但我很确定我错误地访问了对象数组中的数组。

quizEditor: [...state.quizEditor][action.payload.questionId].questionOptions

有谁知道访问它的正确方法是什么?非常感谢!

【问题讨论】:

    标签: redux reactjs typescript redux react-redux state


    【解决方案1】:

    由于您使用的是redux-toolkit,它内置了immer,您可以直接改变状态,它会在内部将其转换为不可变的更新

    const reducer = createReducer({
      [actionAddQuestionOption.type]: (state, { payload: { questionId, ...option }}) => {
        const question = state.questionquizEditor(question => question.id === questionId)
        question.questionOptions.push(option)
      }
    })
    

    使其成为不可变更新的方法是这样的

    const reducer = createReducer({
      [actionAddQuestionOption.type]: (state, { payload: { questionId, ...option } }) => ({
        ...state,
        quizEditor: state.quizEditor.map(question =>
          (question.id === questionId
            ? {
              ...question,
              questionOptions: [...question.questionOptions, option],
            }
            : question)),
      }),
    })
    

    【讨论】:

    • 大家好,感谢 AsafAviv 的帮助,我已经解决了这个问题,无论哪种方式,只要把它留在这里作为一个工作示例,以防有人发现它有用:@987654323 @
    【解决方案2】:

    Arraypush 方法返回数组的新长度,而不是数组本身。您可以做的只是concat 数组的新对象,这反过来将返回带有新问题选项的新数组。

    [...state.quizEditor][action.payload.questionId].questionOptions.concat({
            id: action.payload.id,
            optionText: action.payload.optionText,
            isValid: action.payload.isValid,
         })
    

    此外,我们只需要使用新数组修改 state 中的那个属性:

    const reducer = createReducer({
      //...}, {
      [actionAddQuestionOption.type]: (state, action) => {
        const quizEditor = [...state.quizEditor];
        quizEditor[action.payload.questionId].questionOptions = quizEditor[
          action.payload.questionId
        ].questionOptions.concat({
          id: action.payload.id,
          optionText: action.payload.optionText,
          isValid: action.payload.isValid
        });
    
        return {
          ...state,
          quizEditor
        };
      }
    });
    

    感谢 immer in redux 工具包,我们可以使它更具可读性:

    const reducer = createReducer({
      //...}, {
      [actionAddQuestionOption.type]: (state, action) => {
        const question = state.quizEditor[action.payload.questionId];
        question.questionOptions = [
          ...question.questionOptions,
          {
            id: action.payload.id,
            optionText: action.payload.optionText,
            isValid: action.payload.isValid
          }
        ];
    
        return state;
      }
    });
    

    【讨论】:

    • 是的,这对我的忽视不利。实际上是通过编辑我的问题进行了一半。无论哪种方式,遗憾的是,即使使用您的解决方案,我仍然会收到与我原来的问题中提到的类似的类型错误
    • 是的,我现在知道问题所在了!给我一秒钟重写解决方案:)
    • 这应该返回修改后的状态。而不是只有state 我猜最简单的方法是将数组拼接到optionId 并传播...until splice, new item, ...after splice
    • 它正在返回修改状态。感谢 Immer,我们可以做到这一点,并且无论如何都会得到一个不可变的结果。查看他们的docs 了解更多信息。
    猜你喜欢
    • 2019-02-03
    • 2018-04-30
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 2019-09-25
    • 2021-11-09
    • 2017-10-01
    • 1970-01-01
    相关资源
    最近更新 更多