背景
reactとはmeta(旧facebook)が開発しているjavascriptの操作画面作成ライブラリです。reactが提供するuseState利用すると、値を更新後にその値を利用して画面を再描画してくれます。
reactに慣れているつもりでしたが久々にreactを利用して画面を作ったところ意図せぬuseStateの挙動に戸惑ったので、備忘録として注意点を残します。
useStateの変数が配列の場合は元の値を変えない方式を使う(追加はpushではなくconcatかドットによる展開)
Updating arrays without mutationで公式が説明と利用例を提示してくれています。javascriptの配列の要素変更で使われるpush、pop、reverseなどは、元の配列を変更する形式なため、その操作を行うと意図しない挙動が起こり得ます。
ドットを利用した展開で配列をコピーしてから操作するか、変更後の配列を返してくれるconcatなどの関数を使うのが良いです。
const [valArray, setValArray] = useSttate([])
// Bad
valArray.push(1)
setValArray(valArray)
// OK
setValArray(valArray.concat(1))
// OK
var newValArray = [...valArray]
newValArray.push(1)
setValArray(newValArray)
呼び出し場所によってはuseStateの変数が初期値のまま
useStateとuseEffectを組み合わせてsetIntervalで1秒毎に値が変わる処理を下記のように記述すると、初期値+1の値しか生成されません。// Bad上記の不具合はsetIntervalがstateを考慮しない(第2引数が空の配列)useEffect内で定義されているため、setIntervalの中で呼ばれる変数が初期値のままになるのが原因です。
const [count, setCount] = useState(0)
useEffect(() => {
const idIntervalCountup = setInterval(() => {
setCount(count + 1) // 初期値+1にしかならない
}, 1000)
return () => {
clearInterval(idIntervalCountup)
}
}, [])
自分が把握する解決方法は2つあります。
解法1: 関数形式で値を更新
setの代入を現在の値を第1引数として受け取る関数として行うと期待通りに動きます。// OK with functional updateなお、上記の関数を利用する形式の値更新でも、配列は元の値を壊さない方式で更新が必要です。
const [count, setCount] = useState(0)
useEffect(() => {
const idIntervalCountup = setInterval(() => {
setCount((count) => { return count + 1 }) // <<== 変更箇所
}, 1000)
return () => {
clearInterval(idIntervalCountup)
}
}, [])
参考: My initializer or updater function runs twice
解法2: useEffectの更新対象にstateの変数を設定
useEffectの更新対象としてstateの変数を渡す方式も一手です。しかしながら、変数更新の度にjavascriptの時限式の処理を作り直すことになるので、自分は先程紹介した関数を利用した更新方法の方が好みです。
// OK with set vale to scope of useEffect
const [count, setCount] = useState(0)
useEffect(() => {
const idIntervalCountup = setInterval(() => {
setCount(count + 1)
}, 1000)
return () => {
clearInterval(idIntervalCountup)
}
}, [count]) // <<== 変更箇所
おわり
「reactの基本機能のusetStateが意図するように動かない、なぜだ…」と思わぬ時間を取られましたが、配列などのobjectは元の値を壊さないように、素のjavascriptで実行させる処理は関数形式の値更新をするか値が変わるごとにjavascriptに埋め込む処理を更新すれば良いと分かりました。参考
useStateuseEffect
My initializer or updater function runs twice
0 件のコメント :
コメントを投稿