stateを持ち上げる
兄弟どうしは state を見せ合えない
作品ギャラリーに、種類でしぼり込むボタンを付けたいとします。部品ツリーはこうなります。
App
├─ FilterButtons いま選ばれている種類を切り替える
└─ WorkList 選ばれている種類の作品だけを出すFilterButtons と WorkList は親が同じ、いわば兄弟です。ここで 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 を、親へ持ち上げます。