はじめに
Reactのkey
プロパティと言えば、通常はリスト要素を一意に特定するために利用しますね。
実は、その特性を応用して状態(state)をリセットする方法があります。
本記事では、実際にあった事例をベースに、その方法について紹介します。
状態が更新されない問題
以前、あるライブラリのコンポーネントを利用していた際、「渡す値を更新しても表示が切り替わらない」という問題が発生しました。
その時の内容を簡易化して再現したものを共有します。
問題のコード
CountMonitor
コンポーネントにカウントを渡し、カウントをボタンで増やすという単純なものです。
import { useState } from 'react'
import './App.css'
const App = () => {
const [count, setCount] = useState(0)
return (
<>
<h1>Key Usage Sample</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
Count UP!
</button>
<CountMonitor count={count} />
</div>
</>
)
}
// これは実際には外部ライブラリ内のコード
const CountMonitor = (props: { count: number }) => {
const displayNumber = useState(props.count)
return (<>
<div className="card">
現在のカウントは「<>{displayNumber}</>」です。
</div>
</>)
}
export default App
問題発生時の動作
問題のコードを実行したものが以下です。
ボタンを押しても『現在のカウントは「0」です。』のままで、表示が更新されません。
原因
原因はライブラリ内の以下のコード
const displayNumber = useState(props.count)
Reactでは、props
が更新されてもコンポーネントの内部状態(useState
で管理される状態)は維持される特性があります。
useStateの初期化(上記のコード)は、コンポーネントの初回レンダリング時にのみ実行されます。そのため、props
が更新されても、再レンダリング時に初期値が再評価されないため、更新された値が反映されません。
解決方法
コンポーネントの作りがイケてないので、修正したいところですが、外部ライブラリのためそうはいきません。
そこで利用できるのがkey
プロパティです。
key
プロパティはコンポーネントが同一のものかの判断に使われます。
つまり、この値が変わるとReactが別のものになったと判断します。
これにより、CountMonitor
再作成され、その際に新しいprops
の値が正しく初期化に使用されるようになります。
改善版のコード
改善版では、CountMonitor
のkey
プロパティにcount
の値を利用して一意なキーを指定しています。これにより、カウントが変わるたびに新しいコンポーネントが生成されます。
import { useState } from 'react'
import './App.css'
const App = () => {
const [count, setCount] = useState(0)
return (
<>
<h1>Key Usage Sample</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
Count UP!
</button>
- <CountMonitor count={count} />
+ <CountMonitor key={`counter-${count}`} count={count} />
</div>
</>
)
}
// これは実際には外部ライブラリ内のコード
const CountMonitor = (props: { count: number }) => {
const displayNumber = useState(props.count)
return (<>
<div className="card">
現在のカウントは「<>{displayNumber}</>」です。
</div>
</>)
}
export default App
改善版の動作
改善版のコードを実行したものが以下です。
ボタンを押した時にカウントが増えるようになっていますね!
これは、ボタンをクリックしてカウントを増やすたびに、CountMonitorコンポーネントが新しいkeyを持つため、Reactは古いコンポーネントを破棄し、新しい状態で再生成します。
これにより、useStateが毎回初期化されるようになりました。
注意点
今回の事例はkey
プロパティの理解を深めるために紹介しました。
ただ、この方法では毎回コンポーネントが破棄されて再生成されるため、大量の更新が発生するケースではパフォーマンスが低下する可能性があります。
あくまで止むを得ない場合の対応として活用しましょう。
まとめ
key
プロパティはリスト表示以外の使い方を紹介しました。
この記事で少しでも理解が深まりましたら幸いです。