一つ前の値が表示される
中段 / 目安 30分
琥珀さんが検索欄に文字を打ちながら、首をかしげています。「検索欄、『こ』って打つと空欄の結果、『こは』って打つと『こ』の結果……常に一文字ぶん過去を生きてるんだ」。
打った文字そのものは className が echo の段落に正しく出ています。ずれているのは結果の一覧だけです。1回打つごとに、1つ前の検索語で絞り込んだ結果が出てきます。
呪いの正体は set 関数は予約であって、即時の代入ではない という React の性質です。
JavaScript (JSX)
setWord(e.target.value);
console.log(word); // まだ古い値setWord を呼んだ瞬間に word という変数が書きかわるわけではありません。React に「次の描画ではこの値にしてください」と伝えているだけです。いま動いている関数の中の word は、この描画が始まったときの値のまま、最後まで変わりません。
これは不便な仕様に見えますが、そうではありません。1回の描画の間、同じ名前が途中で別の値に化けないからこそ、コードは上から下へ素直に読めます。React はこの性質を保つために、set をあえて予約にしています。
では、更新後の値はどこで手に入るのか。答えは2つあります。
1つめは 次の描画を待つ ことです。set した値が反映された word は、次にコンポーネント関数が呼び直されたときに手に入ります。効果や描画の中で使うぶんにはこれで足ります。
2つめ、そして今回いちばん素直なのは、イベントが持ってきた値をそのまま使う ことです。入力欄が変わったときに渡ってくる e.target.value は、いま打たれたばかりの新しい文字です。state を経由せず、その値で絞り込んでしまえば、一文字ぶんの遅れは起きません。
JavaScript (JSX)
const next = e.target.value;
setWord(next);
// この next を使って絞り込むstate をいったん名前の付いた変数に受けておくのが、この手の呪いを二度と呼ばないコツです。set に渡すのも絞り込みに使うのも同じ next になるので、片方だけ古いという状態が作れなくなります。
完成条件
- 入力欄に打った文字と、
classNameが hit の要素に出る結果が常に一致する - 「こ」と打った直後に、こはく堂とこまち食堂の2件が出ている
- 続けて「みど」と打つと、結果の先頭がみどり書房に入れ替わる
- 検索語の表示 (
classNameが echo の段落) は今までどおり打った文字を出す
進め方
まず症状を正確に言葉にしてください。「結果が出ない」ではなく「1つ前の検索語の結果が出る」です。ここまで言えていれば、犯人は「古い値を読んでいる場所」だと絞れます。
絞り込みの行に console.log(word, e.target.value) を入れて、2つを並べて見るのが一番早い切り分けです。片方だけ遅れているのが目で見えます。
直すのは1つの関数の中だけです。state の持ち方も画面の作りも変える必要はありません。
ヒントは3段階で、①方針、②使う構文、③部分解の順に出ます。1つも開けずに通せたら、その問題は自分のものです。
供養が済むと、カメ師範が奥から言います。「stateは未来への手紙だ。書いた直後に読み返しても、まだ届いていない」。
ヒント
前のヒントを開くと次が開きます。開かずに解けると未開封クリアです