为什么当我正在传递一个新对象时,反应会认为我正在改变状态?我错过了什么吗? 这是我的减速器,用于在测验数组中的测验对象上添加问题。
case types.UPDATE_QUESTION_SUCCESS: {
let quizContext = state.filter(quiz => quiz.id === action.quizId);
let filteredQuestions = quizContext[0].questions.filter(question =>
question.questionId !== action.question.questionId
);
filteredQuestions.push(action.question);
quizContext[0].questions = filteredQuestions;
let quiz = quizContext[0];
return [...state.filter(quiz => quiz.id !== action.quizId), Object.assign({}, quiz)];
}
错误:
Invariant Violation: A state mutation was detected between dispatches, in the path `quizes.4.questions.1`. This may cause incorrect behavior. (http://redux.js.org/docs/Troubleshooting.html#never-mutate-reducer-arguments)
答案 0 :(得分:2)
首先,由于您只关心quizContext[0]
,filter
有点矫枉过正。
只是做:
const quiz = state.find(quiz => quiz.id === action.quizId);
获得相同的结果。
我不想在这里做太多的代码审查,但还有一件事。即使您应始终使用let
,也要使用const
。 const
只是意味着你无法重新分配你仍然可以改变它的变量。
然后你过滤问题添加一个,然后将其添加回刚刚找到的测验。
quizContext[0].questions =
是个问题,因为即使您的对象现在处于一个新数组中,它们仍然是相同的对象,所以如果你改变它们就会改变状态。
const quiz = Object.assign({}, state.find(quiz => quiz.id === action.quizId));
所以改写完整的东西,你最终得到这样的东西:
case types.UPDATE_QUESTION_SUCCESS:
{
const quiz = Object.assign({}, state.find(quiz => quiz.id === action.quizId));
quiz.questions = quiz.questions.filter(question =>
question.questionId !== action.question.questionId
);
quiz.questions.push(action.question);
return [...state.filter(quiz => quiz.id !== action.quizId), quiz];
}
但请记住,大多数情况下,您根本不想过滤减速机中的数据。相反,我建议您使用reselect,以便您可以根据商店中的数据计算衍生数据。
答案 1 :(得分:0)
您正在根据减速器改变状态。当您使用过滤器函数时,传递给过滤器的对象是原始对象的引用,您不在此处执行deepCopy。这一行filterQuestions.push(action.question)
实际上正在改变状态。
试试这个:
let quizContext = cloneDeep(state.filter(quiz => quiz.id === action.quizId));
虽然可能有一个更优雅的解决方案,但这是你出错的地方。返回的过滤对象是对原始数组的引用,但不是新对象。所以你最终改变了状态中的原始对象。
- 更多澄清 -
cloneDeep
是一个lodash函数,如果你使用Object.assign
,它只会进行浅合并,而不是深度合并。因此,当状态在更深层次上发生变异时,通过改变原始状态,引用仍然是原始对象
- 编辑2 -
正如mash所说,改变你的状态的行是quizContext[0].questions = filteredQuestions;
并使用Object.assign
如果你确定没有嵌套状态,在这种情况下会有更多的reducers