1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactで状態をホイスティング(巻き上げ)する

Last updated at Posted at 2025-03-21

ほぼ Material の TreeView 専用かも

何を解決したいか

以前のReactデモアプリではツリー構造の部署を選択すると部署に所属する社員が表示される際に、ツリーの展開状態は失われてしまう。以下のような感じ。

この状態(改善前)のデモコードは以下のリポジトリから参照することができる

これを改善し、部署選択時にもツリーの展開状態を保持できるようにしたい

コンポーネントの構造と問題の起きる仕組み

デモアプリのコンポーネントは以下のような構造になっている。

Qiita20250321-Reactの状態ホイスティング.png

部署を選択したときの挙動は以下のようになる

  • Folderコンポーネントをクリックして部署を選択すると、propで渡されているsetOrg()が呼び出される
  • setOrg()は定義元であるAppコンポーネント上で実行され、org stateが更新される
  • stateが更新されると、Appコンポーネントは再描画され、子コンポーネントは破棄再作成される
  • 再作成されたコンポーネントのstateは初期状態、つまり閉じている状態になる

図にすると以下のようになる

Qiita20250321-Reactの状態ホイスティング (1).png

状態のホイスティング(巻き上げ)による解決

これを解決するには、Folderコンポーネントが持っている開閉状態を、破棄されないコンポーネントつまりAppコンポーネントに移譲することで行うことができる。本来のコンポーネントが持つ状態を親に渡すので、状態のホイスティング、巻き上げと呼ぶ。

具体的には以下のようにする

  • AppコンポーネントにuseMemoを使って関数 stateStore を作る。stateStore の中のプライベート変数に実際の状態が保存される
  • stateStore をpropでFolderまで伝播させる
  • Folderコンポーネントでは setState(stateStore.get(パス名)) とすることでstateStoreに保存された値をstateの初期値とする
  • ツリーが開閉されるたびに stateStore.set(パス名, 開閉状態) を呼び出すことで、親コンポーネント内に状態を保存することができる

図にすると以下のようになる

Qiita20250321-Reactの状態ホイスティング (2).png

上記を実装したコードは以下の場所で見ることができる(branch: 20250321_hoisted)

修正後の動作は以下のようになる

余談

状態のホイスティング(巻き上げ)は Android JetPack Compose の開発の際に出てくるプログラミングパターンだが、MDNでも言及されている

理解しているのであればMDNの解説でもわからなくもないかもしれないけど、全く知らない人は読んでも何がいいのかよくわからないと思う。

おわりです

Jotai使うのでももちろんいいと思う。ちょっとした状態保存なら別のライブラリ使わなくてもできますよってことを言いたかったです。

1
1
1

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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?