フリーランスでWeb制作をしている矢野です。
前回は、Reactを基礎から学び直すことにした理由を書きました。
今回からは実際に基礎を整理していきます。
まずはシリーズの土台になる、コンポーネント・Props・Stateの3つです。
そもそも「コンポーネント」とは
Reactにおけるコンポーネントは、UIの一部を返す関数のことです。
以前はクラスで書くこともありましたが、今はほとんどの場面で関数コンポーネントを使います。
RE:FITでも、診断の各質問画面をそれぞれ1つのコンポーネントとして分けて実装しました。
たとえば「トレーニングの目的は?」という最初の質問は、GoalQuestionという1つのコンポーネントです。
Props:親から子へデータを渡す仕組み
Propsは、親のコンポーネントから子のコンポーネントに値を渡すための仕組みです。
GoalQuestionのコードを見てみます。
type Props = {
value: Goal | null;
onSelect: (goal: Goal) => void;
onBack: () => void;
};
export default function GoalQuestion({ value, onSelect, onBack }: Props) {
// ...
}
value(今選ばれている目的)、onSelect(選択されたときの処理)、onBack(戻るときの処理)の3つを、親のコンポーネントから受け取っています。
ポイントは、GoalQuestion自身はこれらの値を書き換えられないということです。
選択された、という事実をonSelect経由で親に伝えるだけで、実際に値を更新するのは親側の役目になります。
このときは何となく「そういうものだ」で書いていたのですが、あらためて整理すると、子コンポーネントは親から預かったデータを表示・通知するだけの窓口だと考えると腑に落ちました。
State:コンポーネントが自分で持つ値
一方でStateは、コンポーネント自身が管理する値です。
RE:FITの予約カレンダー画面BookingScreenでは、カレンダーのデータをコンポーネント内のStateとして持たせています。
const [calendar] = useState<CalendarDay[]>(() =>
generateCalendar(new Date())
);
このカレンダーのデータは、診断結果や予約日時のように他のコンポーネントと共有する必要がありません。
BookingScreenが表示されるたびに生成すればいい、その場限りのデータです。
だからこそ、Propsで親から受け取るのではなく、コンポーネント自身のStateとして持たせています。
PropsとStateの境界線をどう引いたか
書きながら気づいたのは、「このデータは誰が持つべきか」を都度考える必要があるということです。
RE:FITでは、診断の回答や予約日時のように画面をまたいで使うデータは、個々のコンポーネントのStateではなく、後述する仕組みで一箇所にまとめて管理しています。
反対に、BookingScreenのカレンダーのように、その画面の中だけで完結するデータはコンポーネント自身のStateに閉じています。
「複数の画面で必要か、その場限りか」が、PropsとStateはもちろん、State自体をどこに置くかを判断する基準になっていました。
この「複数の画面をまたぐ状態をどう管理するか」は、シリーズ第4回であらためて詳しく書く予定です。
まとめ
今回は、コンポーネント・Props・Stateという3つの基礎を、RE:FITの実際のコードを見ながら整理しました。
Propsは親から子へのデータの受け渡し、Stateはコンポーネント自身が持つ値、という役割の違いがポイントです。
次回は、Stateを実際に扱うためのHooks、特にuseStateとuseEffectの実践的な使い方を書きます。