useState
での配列変化の検知
React ではuseState
を用いて、コンポーネントにstate
変数を紐づけることによって状態を管理できます。
このstate
変数には配列を指定することもできます。
ただし、useState
の第2引数として返されるset 関数は、配列に変更が入ったことをObject.is()
によって検知しているため、すでにある配列に対して操作を行っても変更が検知されず、状態の変更が反映されないことがあります。
Object.is()
は厳密等価演算子===
とほとんど同じ判定をしますが、0
と-0
を一致と判定しない点と、NaN
同士を一致判定する点で異なっています。
変数に配列を代入するとき、変数には配列自体ではなく配列への参照(格納されているメモリのアドレスのこと)が代入されます。
配列に変更を加えるメソッドの中には、(参照を変えることなく)配列を直接変更するメソッドがいくつかあり、それらを使用してもReact 側へは配列に加えた変更が伝わらないこととなります。
Object.is()
と配列
変数に配列を代入するとき、変数には配列自体ではなく配列への参照が代入されます。
そのため、以下のように同じ要素を持つ配列を別々に宣言しても、Object.is()
には一致していると判定されません。
const arr = [1, 2, 3, 4]
const arr2 = [1, 2, 3, 4]
console.log(Object.is(arr, arr2))
// false
しかし、別の変数に配列を代入してからその配列に要素を直接追加して、元の変数と比較したときには一致判定がされます。
const initialState = ['a', 'b', 'c']
const state = initialState
state[3] = 'd'
console.log(state)
// [ 'a', 'b', 'c', 'd' ]
console.log(Object.is(state, initialState))
//true
配列に要素を直接追加しても、Object.is()
には元と同じ配列であるとみなされて変更が検知できないわけですね。
state
に配列を格納して値を変更するときには、その変更が検知されるような書き方をする必要があります。
新たな配列を返すメソッド
配列を操作するメソッドのうち、変更結果を新しい配列として返すメソッドがいくつか存在します。
基本的にはそれらのメソッドを使用するようにすることで、配列への変更が検知されない事態を避けることができます。
配列操作後に新たな配列を返すメソッドとしては、concat()
やmap()
、filter()
やslice
などが挙げられます。
配列への追加や削除、更新などの用途に応じて以下でみていきます。
配列への追加
スプレット構文やconcat
、push
やunshift
など
配列への更新
map
やsplice
など
配列要素の削除
filter
やslice
、splice
など
配列要素の並び替え
sort
やreverse
など
直接変更を加えるメソッドを使うには
変更を加えたい配列をあらかじめ複製しておき、複製した配列に対して操作を加えるようにすれば、直接変更を加えるメソッドでも問題なく使用できるので安心です。