概要
ReactのuseStateフックを使って管理している多次元の配列やオブジェクトを更新する場合、元の配列・オブジェクトをディープコピーしなければならない。
シャローコピーだと破壊的変更となってしまい、StrictモードではsetStateが2回呼び出されてしまう。
例
検証用コード
import {useCallback, useState} from 'react'
export default function App() {
const [arr, setArr] = useState([0]);
const [arr2, setArr2] = useState([[0]]);
const [obj, seObj] = useState({num:0});
const [obj2, seObj2] = useState({sub: {num:0}});
const handleClick = () => {
setArr((prevArr)=>{
const newArr= [...prevArr]
newArr[0]++
return newArr;
})
setArr2((prevArr2)=>{
const newArr2= [...prevArr2] // シャローコピー
newArr2[0][0]++
return newArr2;
})
seObj((prevObj)=>{
const newObj= {...prevObj}
newObj.num++
return newObj;
})
seObj2((prevObj2)=>{
const newObj2= {...prevObj2} // シャローコピー
newObj2.sub.num++
return newObj2;
})
}
return (
<div className="App">
<p>arr={arr[0]}</p>
<p>arr2={arr2[0][0]}</p>
<p>obj={obj.num}</p>
<p>obj2={obj2.sub.num}</p>
<button onClick={handleClick}>click</button>
</div>
);
}
検証用コード実行結果
arr=1
arr2=2
obj=1
obj2=2
検証用コード(ディープコピーに修正後)
import {useCallback, useState} from 'react'
export default function App() {
const [arr, setArr] = useState([0]);
const [arr2, setArr2] = useState([[0]]);
const [obj, seObj] = useState({num:100});
const [obj2, seObj2] = useState({sub: {num:100}});
const handleClick = () => {
setArr((prevArr)=>{
const newArr= [...prevArr]
newArr[0]++
return newArr;
})
setArr2((prevArr2)=>{
const newArr2 = JSON.parse(JSON.stringify(prevArr2)) // ディープコピー
// または const newArr2 = prevArr2.map(subArr => [...subArr])
newArr2[0][0]++
return newArr2;
})
seObj((prevObj)=>{
const newObj= {...prevObj}
newObj.num++
return newObj;
})
seObj2((prevObj2)=>{
const newObj2 = JSON.parse(JSON.stringify(prevObj2)) // ディープコピー
newObj2.sub.num++
return newObj2;
})
}
return (
<div className="App">
<p>{arr[0]}</p>
<p>{arr2[0][0]}</p>
<p>{obj.num}</p>
<p>{obj2.sub.num}</p>
<button onClick={handleClick}>click</button>
</div>
);
}
検証用コード(ディープコピーに修正後)実行結果
arr=1
arr2=1
obj=1
obj2=1
補足
JSON.parse(JSON.stringify(xxx))
でのディープコピーは、Date型やFunction型などの値には使うことができない。
JavaScriptのDeepCopyでJSON.parse/stringifyを使ってはいけない - Qiita
JavaScriptでのDeepCopyはJqueryではextend, angular.js だと angular.copy を使って簡単にできてしまうようだけれど、標準実装としてはそもそも用意が…
参考サイト
Attention Required! | Cloudflare
関連書籍
モダンJavaScriptの基本から始める React実践の教科書
posted with ヨメレバ
じゃけぇ(岡田 拓巳) SBクリエイティブ 2021年09月18日頃