Mutation と Immutable Update
Mutation とは
既存のオブジェクトや配列をその場で変更すること。
例
const user = {
name: 'Max'
};
user.name = 'Anna'; // mutation
const numbers = [1, 2, 3];
numbers.push(4); // mutation
gameBoard[0][0] = 'X'; // mutation
Immutable Update とは
既存のオブジェクトや配列を変更せず、
新しいオブジェクトや配列を作って更新すること。
例
const user = {
name: 'Max'
};
const updatedUser = {
...user,
name: 'Anna'
};
const numbers = [1, 2, 3];
const updatedNumbers = [...numbers, 4];
比較
Mutation(破壊的変更)
const user = {
name: 'Max'
};
user.name = 'Anna';
メモリ上の同じオブジェクトを書き換える。
oldUser === user; // true
Immutable Update(非破壊的更新)
const user = {
name: 'Max'
};
const updatedUser = {
...user,
name: 'Anna'
};
新しいオブジェクトを作る。
oldUser === updatedUser; // false
Reactで問題になる Mutation
悪い例(State Mutation)
const [user, setUser] = useState({
name: 'Max'
});
user.name = 'Anna';
setUser(user);
既存の state を直接変更している。
React が変更を検知できない可能性がある。
良い例(Immutable Update)
setUser(prevUser => ({
...prevUser,
name: 'Anna'
}));
新しいオブジェクトを作って state を更新している。
Local Mutation は問題ない
Mutation 自体が悪いわけではない。
新しく作ったローカル変数を変更するのは普通に OK。
OKな例
function createUser() {
const user = {};
user.name = 'Anna';
return user;
}
これは mutation だが、
user
は関数内で新しく作られたオブジェクトなので問題ない。
三目並べの例
function deriveGameboard(gameTurns) {
const gameBoard = [
[null, null],
[null, null]
];
for (const turn of gameTurns) {
gameBoard[turn.row][turn.col] = turn.player;
}
return gameBoard;
}
この
gameBoard[turn.row][turn.col] = turn.player;
も mutation。
しかし
const gameBoard = ...
で毎回新しく作った配列なので問題ない。
React界隈での典型的な表現
Don't mutate state.
State を直接変更するな。
Use immutable updates.
非破壊的な更新を使え。
State should be treated as immutable.
State は immutable(不変)として扱うべき。
Avoid mutating objects and arrays stored in state.
State に保存されているオブジェクトや配列の mutation を避けるべき。
覚え方
mutation
= 既存データを書き換える
= 破壊的変更
immutable update
= 新しいデータを作る
= 非破壊的更新
React が嫌うのは Mutation そのものではない。
React が嫌うのは State Mutation。
つまり
Local Mutation → OK
State Mutation → NG