2
1

More than 1 year has passed since last update.

【React】 Stateのリフトアップとは

Posted at

環境

React 18.2.0

はじめに

Reactは双方向バインディングではなく、単方向のデータフローを採用しています。
双方向バインディングだと、データの状態がどのように変更されているかや、状態管理が複雑になる問題がある一方、単方向データフローだとその状態を管理しているコンポーネントのみが変更することができるので、デバッグや状態管理が容易になるという利点があります。

そのためコンポーネント間で状態を管理する場合は、「Stateのリフトアップ」をする必要があります。
要するに「Stateのリフトアップ」とは複数のコンポーネント間で共有される状態を、上位の親コンポーネントで管理することです。

サンプル

今回は例として、郵便番号を入力し完了次第、都道府県、市が表示されるという画面を作成します。
スクリーンショット 2023-02-04 22.13.41.png

親コンポーネントから作成していきます。

Parent.jsx
import { Card } from "./Card";
import { ZipCodeInputText } from "./ZipCodeInputText";

export const Parent = () => {
  return (
    <>
      <ZipCodeInputText />
      <Card />
    </>
  );
};

そして子コンポーネントに該当する郵便番号のInput要素と、結果を表示するコンポーネントを作成します。

ZipCodeInputText.jsx
import { useState } from "react";

export const ZipCodeInputText = () => {
  const [zipcode, setZipCode] = useState("");

  return (
    <div style={{ margin: "10px 0" }}>
      <label htmlFor="zipcode" style={{ marginRight: "10px" }}>
        郵便番号 :
      </label>
      <input
        name="zipcode"
        type="text"
        value={zipcode}
        onChange={(event) => setZipCode(event.target.value)}
      />
    </div>
  );
};
Card.jsx
const Addresses = [
  {
    zipcode: "100001",
    prefecture: "東京都",
    city: "千代田区"
  }
];

export const Card = ({ zipcode }) => {
  const address = Addresses.find((address) => address.zipcode === zipcode);

  return (
    <div>
      <p>都道府県 : {address?.prefecture}</p>
      <p>市 : {address?.city}</p>
    </div>
  );
};

ZipCodeInputTextでは郵便番号を、コンポーネントの内部で制御されるStatezipcodeとして定義して管理しています。
ここで今回の仕様について確認しましょう。
都道府県と市は郵便番号によって、表示されるという仕様でした。
そのため、Cardコンポーネントは郵便番号という状態を知る必要がありそうです。
これがコンポーネント間で状態を管理するケースに該当します。
現時点の実装ではこれらの仕様を再現するのは厳しいため、「Stateのリフトアップ」利用して実装し直していきましょう。

実装

やり方は簡単です。導入で説明した通り、上位の親コンポーネントで管理すれば良いのです。
それでは実装していきましょう。

Parent.jsx
import { useState } from "react";
import { Card } from "./Card";
import { ZipCodeInputText } from "./ZipCodeInputText";

export const Parent = () => {
  const [zipcode, setZipCode] = useState("");

  const updateZipCode = (zipcode) => {
    setZipCode(zipcode);
  };

  return (
    <>
      <ZipCodeInputText update={updateZipCode} />
      <Card zipcode={zipcode} />
    </>
  );
};
ZipCodeInputText.jsx
export const ZipCodeInputText = ({ update }) => {
  return (
    <div style={{ margin: "10px 0" }}>
      <label htmlFor="zipcode" style={{ marginRight: "10px" }}>
        郵便番号 :
      </label>
      <input
        name="zipcode"
        type="text"
        onChange={(event) => update(event.target.value)}
      />
    </div>
  );
};

Cardコンポーネントは変更がないためそのままです。
郵便番号を親コンポーネントであるParentで定義し、更新関数を渡して入力された郵便番号で更新するようにしました。
これによって、コンポーネント間で状態を管理することが可能になりました。

おわりに

「Stateのリフトアップ」という言葉をよく聞き、その実体についてあまりわかっていなかったので今回記事にしました。
おそらくほとんどの人が無意識のうちにやっていることだと思います。私もそうでした。
ただ複雑なUIになってくるとグローバルで管理しようとしたりするので、その際は「「Stateのリフトアップ」で解決できないか検討しましょう。
そうすることで、状態の管理がしやすくなったり、再利用しやすいコンポーネントを作成することができるため、保守性の向上にもつながるかと思います。

参考ドキュメント

2
1
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
2
1