1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ReactAdvent Calendar 2024

Day 8

[React] keyプロパティを利用してstateをリセットする

Last updated at Posted at 2024-12-11

はじめに

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

問題発生時の動作

問題のコードを実行したものが以下です。

test.gif

ボタンを押しても『現在のカウントは「0」です。』のままで、表示が更新されません。

原因

原因はライブラリ内の以下のコード

const displayNumber = useState(props.count)

Reactでは、propsが更新されてもコンポーネントの内部状態(useStateで管理される状態)は維持される特性があります。
useStateの初期化(上記のコード)は、コンポーネントの初回レンダリング時にのみ実行されます。そのため、propsが更新されても、再レンダリング時に初期値が再評価されないため、更新された値が反映されません。

解決方法

コンポーネントの作りがイケてないので、修正したいところですが、外部ライブラリのためそうはいきません。
そこで利用できるのがkeyプロパティです。

keyプロパティはコンポーネントが同一のものかの判断に使われます。
つまり、この値が変わるとReactが別のものになったと判断します。
これにより、CountMonitor再作成され、その際に新しいpropsの値が正しく初期化に使用されるようになります。

改善版のコード

改善版では、CountMonitorkeyプロパティに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

改善版の動作

改善版のコードを実行したものが以下です。

test2.gif

ボタンを押した時にカウントが増えるようになっていますね!

これは、ボタンをクリックしてカウントを増やすたびに、CountMonitorコンポーネントが新しいkeyを持つため、Reactは古いコンポーネントを破棄し、新しい状態で再生成します。
これにより、useStateが毎回初期化されるようになりました。

注意点

今回の事例はkeyプロパティの理解を深めるために紹介しました。
ただ、この方法では毎回コンポーネントが破棄されて再生成されるため、大量の更新が発生するケースではパフォーマンスが低下する可能性があります。
あくまで止むを得ない場合の対応として活用しましょう。

まとめ

keyプロパティはリスト表示以外の使い方を紹介しました。
この記事で少しでも理解が深まりましたら幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?