2016-07-23 20 views
1

明らかに私が新しいオブジェクトを渡しているときに、反応が私が状態を突然変異していると思うのはなぜですか?何か不足していますか? これは、クイズの配列内のクイズオブジェクトに質問を追加することを減らすものです。Redux状態の突然変異

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

還元剤の全機能を投稿してください。 – Timo

答えて

2

まず順になっていた場合には、ネストされた状態を持っていないことを確認している場合Object.assignを使用しています。 これを実行すると、同じ結果が得られます。

const quiz = state.find(quiz => quiz.id === action.quizId); 

ここではあまりにも多くのコードレビューをしたくありませんが、もう1つです。いつでもconstを使用する必要がありますが、letを使用してください。 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]; 
} 

しかし、時間のほとんどは、あなたの中のデータをフィルタリングするためにwan'tていないことを心に留めておくと減速機はまったくありません。代わりにreselectを使用して、ストア内のデータから派生データを計算することをお勧めします。

+0

'Object.assign'は浅いマージを行います。状態がネストされた構造を持っている場合、最初の深さを越える参照は実際には古い状態を参照するため、さらなる突然変異につながる可能性があります。 – sasidhar

+0

@sasidharこのケースでは効果がありません。深くネストされた構造を持っている場合は、reduxで深度を扱う新しい減速器を追加します。それ以外はすべて悪い習慣です。 – mash

0

あなたの減速ごとに状態を変異されています。フィルタ関数を使用する場合、フィルタに渡されるオブジェクトは元のオブジェクトの参照であり、ここではdeepCopyを実行していません。そして、この行filterQuestions.push(action.question)は実際に状態を突然変異させています。

代わりにこれを試してみてください。そこに、よりエレガントな解決策があるかもしれません let quizContext = cloneDeep(state.filter(quiz => quiz.id === action.quizId));

が、あなたは間違っている場所です。返されるフィルタされたオブジェクトは、元の配列への参照ですが、新しいオブジェクトは参照されません。だから、あなたは状態の元のオブジェクトを突然変異させることになります。

--More clarification--

cloneDeepあなたは深いマージのみ浅いマージをするつもりはないが、Object.assignを使用している場合lodash機能です。状態はより深いレベルで変異しているときに参照は、あなたの状態が変異されたラインマッシュで述べたように、元の状態

--edit 2--

を突然変異させることによってそこに元のオブジェクトの残っていますquizContext[0].questions = filteredQuestions;と、あなたがたったの約quizContext[0]filterが過剰ですが気にするので、よりレデューサーは、すべての

+0

プッシュ自体は状態を変更しません!cloneDeepこれは関数でも、この場合に既に気づいたように適切な習慣でも適切でもありません。 – mash

+0

@mashプッシュ機能は突然変異を引き起こしている機能です。いいえ?それが持っている参照は、元のオブジェクト内の元のオブジェクト内にあるので、プッシュは実際に元のオブジェクトにプッシュするので、「プッシュ自体は状態を変更しない」と言ったときの意味を理解できませんでした。 ? – sasidhar

+0

いいえ、ECMA-262では 'filterは呼び出されたオブジェクトを直接変更しませんが、callbackfnへの呼び出しによってオブジェクトが変更される可能性があります。 'と述べているので、新しい配列にプッシュすると、新しい配列は 'quizContext [0] .questions'にはなりません。突然変異は彼がそれを再割り当てすると起こる。 – mash

関連する問題