LESSON 07 · 約30分
状態を持ち上げ、二つの部品をつなぐ
同じ情報を使う部品の共通の親にstateを置き、計算できる値を減らします。
コードを試す前に
01のVite reactテンプレート(JavaScript/JSX)を使います。特記のないApp.jsxは、そのレッスンだけで動く独立した例です。例同士を連結しないでください。
短い学習用の例です。各App.jsxは独立した例で、順に追記して一つの完成品にする形式ではありません。ファイル名、依存関係、未定義部分の前提は注記に記載します。問題内の抜粋や空欄は、そのまま実行するための完成コードではありません。
開発環境の準備を確認する →このレッスンでわかること
- 共通の親へstateを移動できる
- 入力stateと計算結果を区別できる
値は一か所を正とする
検索欄と結果一覧が同じキーワードを使うなら、両者の共通の親がqueryを持ちます。子へ値と変更通知の関数を渡し、親で更新します。子ごとに同じ値のstateを置いてEffectで合わせると、ずれや余分な処理が増えます。
計算で出る値はstateにしない
検索結果はitemsとqueryからfilterで計算できます。件数も結果のlengthで分かります。元データ・検索語・検索結果・件数を全部stateにすると、ひとつ更新し忘れたときに表示が矛盾します。最小の状態と、その状態から導く表示に分けましょう。
データは下へ、操作の通知は上へ
子はonQueryChangeのようなpropsを呼び、親に変更を知らせます。子が親の変数を直接書き換えるわけではありません。まずpropsで関係を明示し、受け渡しが深く複雑になってからContextを検討します。
検索欄と一覧を分離する
src/App.jsx
import { useState } from 'react';
const topics = [
{ id: 1, title: 'JSX' },
{ id: 2, title: 'props' },
{ id: 3, title: 'state' },
];
function SearchBox({ query, onQueryChange }) {
return <label>検索語
<input value={query} onChange={event => onQueryChange(event.target.value)} />
</label>;
}
function Results({ items }) {
return items.length === 0 ? <p>一致するテーマがありません。</p> : (
<ul>{items.map(item => <li key={item.id}>{item.title}</li>)}</ul>
);
}
export default function App() {
const [query, setQuery] = useState('');
const normalized = query.trim().toLowerCase();
const visible = topics.filter(topic => topic.title.toLowerCase().includes(normalized));
return (
<main>
<h1>テーマを探す</h1>
<SearchBox query={query} onQueryChange={setQuery} />
<p role="status">{visible.length}件</p>
<Results items={visible} />
</main>
);
}visibleは描画中の計算結果です。検索のたびに通信しない、固定データの小さな例です。
TRY IT YOURSELF
検索欄と一覧をつなぐ
- 大文字・小文字や前後の空白があっても同じ検索結果になるか確かめる
- 一致しない語を入れて0件表示を確認する
- queryを空文字へ戻すクリアボタンを親へ追加する
- stateを持つのが親だけであることをコード上で確認する
できたらOK:入力・件数・結果が常に一致し、Effectで検索結果をコピーしていない
QUICK CHECK
理解を確かめよう
公式資料でもう少し詳しく
ここまで読めたら、ひとつ前進。