https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
1. 不変性と副作用
1. 不変: 常に新しい値を作成して古い値を置き換えます
2. 不変性ルール:
(1) 純粋関数は、同じ入力が与えられた場合、常に同じ値を返さなければなりません
(2) 純粋な関数には副作用がありません
3. 副作用: 即時関数の範囲外で何かを変更する
受信パラメータ(プロパティ)を変更/変更します
グローバル変数など、関数の外部で他の状態を変更する
API呼び出しを行う
console.log()
Math.random()
4. 配列を変更する配列メソッド: プッシュ、ポップ、シフト、シフト解除、ソート、リバース、スプライス
配列に対して不変の操作を実行したい場合は、まず配列をコピーしてから、そのコピーに対して操作を行うことができます。
5. 純粋関数は他の純粋関数のみを呼び出すことができます
2. React は不変性を好む
1. 不変性と PureComponent
React.Component の関数とクラス型を継承する React コンポーネントは、親コンポーネントが再レンダリングするか setState を使用するときに再レンダリングされます。
React.PureComponent を拡張するクラスは、その状態が更新されるか、プロパティが更新されるときにのみ再レンダリングされます。
プロパティが PureComponent に渡される場合、不変の更新が保証される必要があります。これは、内部またはプロパティが直接変更されても参照が変更されない場合、コンポーネントは変更されたことに気付くことができず、再レンダリングされないためです。
2. JS における参照の等価性
オブジェクトと配列はメモリに格納されます。変数が再割り当てされると、変数は新しいメモリ アドレスを指しますが、変数の内部構造のみが変更された場合、変数は依然として同じアドレスを指し、その内容は住所変更。
=== を使用してオブジェクトと配列を比較する場合、本質は参照アドレスを比較すること、つまり「参照の等価性」です。
オブジェクトを変更する場合、オブジェクトの内容は変更されますが、その参照は変更されません。あるオブジェクトを同じメモリ位置を指す別のオブジェクトに割り当てると、2 番目のオブジェクトに対する操作は最初のオブジェクトの値にも影響します。
等価性を詳細にチェックしてみてはいかがでしょうか: 遅い、時間計算量 O(N)
参照の比較: オブジェクトの内部がどれほど複雑であっても、O(1)
3. const は参照の再割り当てを防止するだけで、オブジェクトの内容の変更は防止しません。
4. 考え方: React フレームワークのパフォーマンスを考慮して、複雑なシナリオでパフォーマンスを確保するために、オブジェクトの内部値を 1 つずつ比較するのではなく、参照アドレスを比較して、時間計算量を確保します。各比較は O(1 ) です。したがって、この設計思想から、State を更新するときに、object.property=xxx を通じてオブジェクトの内容を直接変更するのではなく、古い値に基づいて新しい値が返されます (参照アドレスが変更されます)。したがって、ほとんどの言語は object.property=xxx を通じてオブジェクトのプロパティを変更し、React は古い値に基づいて新しい値を返します。
3. Redux で状態を更新します (メインコンテンツ、前回はこれについて説明しています)
1. リデューサーは純粋な関数である必要があり、状態を直接変更することはできません。つまり、状態とアクションを受け入れ、古い状態に基づいて新しい状態を返します。上の部分では可変と不変について説明し、次に状態を不変に更新する方法について説明します。
- オブジェクトスプレッド演算子...: 別のオブジェクトまたは配列とまったく同じ内容を含む新しいオブジェクトまたは配列を作成します。
3. 状態の更新: 受信した this.setState() オブジェクトは浅くマージされます。
redux のリデューサー内:
(1) 通常状態の更新
return { …状態、(ここで更新) }
(2) オブジェクトの最上位のプロパティを更新する場合は、既存の状態をコピーし、...を通じてオブジェクトを展開し、変更するプロパティとその値を追加する必要があります。
function raiser(state, action) { /*状態は次のようになります。
state = {
clicks: 0,
count: 0
}
*/
return { …状態、クリック数: state.clicks + 1、カウント: state.count - 1 } }
(3) オブジェクト内のオブジェクトを更新します 更新したい部分が深層にある場合は、各レベルを展開してコピーを作成する必要があります
function raiser(state, action) { /*状態は次のようになります。
state = {
house: {
name: "Ravenclaw",
points: 17
}
}
*/
// レイブンクローの 2 つのポイント
return { …state, // ステート (レベル 0) をコピーしますhouse: { …state.house, // ネストされたオブジェクト (レベル 1) をコピーしますPoints: state.house.points + 2 } }
(4) キーによるオブジェクトの更新
function raiser(state, action) { /*状態は次のようになります。
const state = {
houses: {
gryffindor: {
points: 15
},
ravenclaw: {
points: 18
},
hufflepuff: {
points: 7
},
slytherin: {
points: 5
}
}
}
*/
//名前が変数に格納されている場合、 // Ravenclaw に 3 ポイントを加算します。
const key = “ravenclaw”;
return { …state, // 州の住宅をコピー: { …state.houses, // 住宅をコピー[key]: { // 特定の 1 つの住宅を更新します (計算プロパティ構文を使用) …state.houses[key], // それをコピーします特定の家のプロパティポイント: state.houses[key].points + 3 // そのプロパティを更新します} } }
points
(5) 配列に項目を追加する
Array.prototype.unshift を介して配列の前後に項目を追加してプッシュすると配列が変更されますが、不変の方法 (オブジェクトの展開とコピー、または .slice によるコピー後にプッシュ) で項目を追加することが望まれます。
function raiser(state, action) { /*状態は次のようになります。
state = [1, 2, 3];
*/
const newItem = 0;
return [ // 新しい配列
newItem, // 最初に新しい項目を追加します
…state // 次に最後に古い状態を展開します 先頭に追加
];
return [ // 新しい配列
newItem, // 最初に新しい項目を追加します
…state // その後、最後に古い状態を展開します 追加到后面
];
//または、.slice を介して配列をコピーし、最後にプッシュします
functionducer(state, action) { const newItem = 0; const newState = state.slice();
newState.push(newItem);
newStateを返します。
(6) マップを介して配列内の項目を更新します
.map を介して各項目をトラバースし、変更する項目を見つけて、返された値を項目の新しい値として使用します。最後に、map は新しい配列を返し、.filter を使用してフィルターします。
function raiser(state, action) { /*状態は次のようになります。
state = [1, 2, "X", 4];
*/
return state.map((item,index) => { // “X” を 3 に置き換えます// あるいは、特定のインデックスを探すこともできますif(item === “X”) { return 3; }
// Leave every other item unchanged
return item;
});
}
(7) 配列内のオブジェクトを更新する
.map を介して各項目をトラバースし、変更するオブジェクトを見つけて、オブジェクトをコピーしてコピーを変更し、それを新しいオブジェクトとして返します。最後に、map は新しい配列を返します。フィルターする必要がある場合は、.filter を使用します。
function raiser(state, action) { /*状態は次のようになります。
state = [
{
id: 1,
email: '[email protected]'
},
{
id: 2,
email: '[email protected]'
}
]
Action contains the new info:
action = {
type: "UPDATE_EMAIL"
payload: {
userId: 2, // Peter's ID
newEmail: '[email protected]'
}
}
*/
// マップは新しい配列を返します
return state.map((item,index) => { // 一致する ID を持つアイテムを検索しますif(item.id === action.payload.userId) { // Return新しいオブジェクト 変更後、新しいオブジェクトが返されますreturn { …item, // 既存のアイテムをコピーします オブジェクトの変更する必要のない部分をコピーしますemail: action.payload.newEmail // 電子メールのアドレスを置き換えます} }
// Leave every other item unchanged
return item;
});
}
(8) 配列の途中に項目を挿入します。
.splice 関数は項目を挿入しますが、配列を変更します。まず、slice を使用して配列をコピーしてから、splice を使用して項目を挿入することも、新しい項目の前に要素をコピーし、新しい項目を挿入して、項目の後に要素をコピーすることもできます。
function raiser(state, action) { /*状態は次のようになります。
state = [1, 2, 3, 5, 6];
*/
const newItem = 4;
// コピーを作成します
const newState = state.slice();
// 新しい項目をインデックス 3 に挿入
newState.splice(3, 0, newItem)
newStateを返します。
/*
// 次の方法でも実行できます。
return [ // 新しい配列を作成します
…state.slice(0, 3), // 最初の 3 つの項目を変更せずにコピーします
newItem, // 新しい項目を挿入します
…state.slice(3) // 残りをインデックスからコピーします3
];
*/
}
(9) 配列内の項目をインデックスで更新する
マップを使用してこの項目を検索し、新しい値を返します
function raiser(state, action) { /*状態は次のようになります。
state = [1, 2, "X", 4];
*/
return state.map((item,index) => { // インデックス 2 の項目を置換if(index === 2) { return 3; }
// Leave every other item unchanged
return item;
});
}
(10) 配列から項目を削除する
フィルターを通過すると、各項目が渡され、判定関数が true を返す項目のみを含む新しい配列が返されます。
function raiser(state, action) { /*状態は次のようになります。
state = [1, 2, "X", 4];
*/
return state.filter((item,index) => { // 項目「X」を削除// あるいは、特定のインデックスを探すこともできますif(item === “X”) { return false; }
// Every other item stays
return true;
});
}