stateを持ち上げる

兄弟どうしは state を見せ合えない

作品ギャラリーに、種類でしぼり込むボタンを付けたいとします。部品ツリーはこうなります。

App ├─ FilterButtons いま選ばれている種類を切り替える └─ WorkList 選ばれている種類の作品だけを出す

FilterButtonsWorkList は親が同じ、いわば兄弟です。ここで FilterButtons の中に useState を書くと、選ばれている種類はその部品の中だけの値になります。WorkList からは見えません。ボタンの見た目は変わるのに一覧はまったく動かない、という状態になります。

state は下から上へ、横へ、と流れません。props で上から下へ流れるだけです。これが React の唯一の向きです。

共通の親へ持ち上げる

解き方は決まっています。その値を必要とする部品すべての、いちばん近い共通の親へ state を移します。これを state の持ち上げ、リフトアップと呼びます。

いまの例なら、共通の親は App です。

import { useState } from "react"; export default function App() { const [category, setCategory] = useState("all"); return ( <div> <FilterButtons category={category} onChange={setCategory} /> <WorkList category={category} /> </div> ); }

App が値を持ち、下へ配ります。WorkList には値そのものを、FilterButtons には値を変える手段を渡します。更新関数もただの関数なので、props として渡せます。

子は預かるだけになる

持ち上げたあとの子は、自分では何も覚えません。

export default function FilterButtons({ category, onChange }) { return ( <div> <button className="filter-all" onClick={() => onChange("all")}>すべて</button> <button className="filter-web" onClick={() => onChange("web")}>web</button> </div> ); }

押されたことを親に伝えるだけです。値をどう変えるかは親が決めます。こうすると子は「渡されたものを描くだけ」の部品になり、どの画面へ持っていっても同じように動きます。

つまずきどころ

持ち上げたのに、子のほうの useState を消し忘れることがよくあります。両方あると、押したときに子の中の値だけが変わり、親から降ってくる値は古いまま、という食い違いが起きます。同じ意味の値を2か所で持たないのが鉄則です。

もうひとつ、持ち上げすぎにも注意します。1つの部品の中でしか使わない値まで親へ上げると、親が全部の state を抱えて読めなくなります。上げるのは、2つ以上の部品が見る値だけです。

演習

子が握っていたしぼり込みの state を、親へ持ち上げます。

ヒント

App.jsx
FilterButtons.jsx
WorkList.jsx
works.js
App.jsx
FilterButtons.jsx
WorkList.jsx
works.js
プレビュー

できているか

  • はじめは「3 件」と表示されている
  • web を押すと「2 件」になる
  • tool を押すと買い物メモツールだけが残る
  • すべて を押すと3件に戻る