React・動きをつける

イベントと入力を受け取る

onClickに関数を渡す書き方、括弧を付けると即実行されてしまう罠、入力欄をvalueとonChangeでReactに預ける「制御コンポーネント」まで。検索ボックスを作って一覧をしぼりこみます。

星を押せるようになりました。今回は入力です。検索ボックスに文字を打つと、一覧がその場でしぼりこまれるようにします。

イベントは「関数を渡す」

ReactではaddEventListenerを書きません。属性として関数を渡します。

<button onClick={handleClick}>おす</button>

HTMLのonclickと違い、キャメルケース(onClickで、渡すのは文字列ではなく関数です。

よく使うイベントは、素のJavaScriptと同じ顔ぶれです。

イベント使う場面
onClickボタン・カードを押す
onChange入力欄の中身が変わった
onSubmitフォームの送信
onKeyDownキーが押された

イベントオブジェクトの受け取り方

関数の第1引数に、イベントの情報が渡ってきます。

function handleChange(event) {
  console.log(event.target.value)   // 入力された文字
}

event.targetイベントが起きた要素.valueその中身。ここは素のJavaScriptとまったく同じです。

フォームの送信でページが再読みこみされるのを止めるのも同じです。

function handleSubmit(event) {
  event.preventDefault()

}
JavaScriptボタンを押したら動かすイベントそのものがあやしい人はこちら。考え方はReactでもそのまま通用します

入力欄はstateに預ける(制御コンポーネント)

素のJavaScriptでは、入力欄の値はDOMが持っていました。Reactではstateに持たせて、入力欄はそれを映すだけにします。

const [keyword, setKeyword] = useState('')

<input
  type="search"
  value={keyword}
  onChange={(event) => setKeyword(event.target.value)}
/>

流れはこうです。

  1. 文字を打つ → onChangeが呼ばれる
  2. setKeywordでstateが更新される
  3. 画面が描き直され、value={keyword}に新しい文字が入る

画面に出ている文字=stateの値が常に保証されるので、検索や送信のときに「いまの入力値」をDOMから取りにいく必要がなくなります。この書き方を制御コンポーネントと呼びます。

チェックボックスはvalueではなくcheckedを使います。

<input
  type="checkbox"
  checked={onlyFavorites}
  onChange={(event) => setOnlyFavorites(event.target.checked)}
/>

検索でしぼりこむ

入力値がstateにあるので、表示する配列を作り直すだけで検索になります。

const shownBirds = birds.filter(
  (bird) => bird.name.includes(keyword) || bird.area.includes(keyword),
)

そして一覧はbirdsではなくshownBirdsmapします。

0件のときを忘れない

検索は何も見つからない状態が必ず起きます。前回の三項演算子の出番です。

{shownBirds.length === 0 ? (
  <p className="mt-6 text-slate-500">みつかりませんでした。</p>
) : (
  <div className="grid …">
    {shownBirds.map((bird) => <BirdCard key={bird.id} bird={bird} />)}
  </div>
)}

「0件のときは何も出ない」は不具合に見えます。「みつかりませんでした」の一文があるだけで、使う人の不安がなくなります。

公式ドキュメントも見てみる

ja.react.dev外部サイトイベントへの応答 – React公式の「イベントへの応答」。「イベントハンドラを追加する」の節に、括弧を付けて渡してしまう間違いの解説があります

このレッスンのまとめ

  1. イベントはキャメルケースの属性に関数を渡すonClick={handleClick}
  2. 括弧を付けると即実行。引数を渡したいときはアロー関数で包む
  3. event.target.valueで入力値を取る——素のJavaScriptと同じ
  4. 入力欄はvalueonChangeをセットにしてstateに預ける(制御コンポーネント)
  5. チェックボックスはcheckedonChange
  6. 絞りこんだ結果はstateにしない——元のデータと検索語から毎回計算する
  7. 0件のときの表示を必ず用意する

次は、いまカードの中に散らばっているお気に入りの状態を、親にまとめます。「お気に入りだけ見る」を作ろうとすると、いまの形では作れないことに気づく回です。

やってみよう:検索ボックスでしぼりこむ

App.jsxに検索ボックスを足します。

  1. App.jsxの先頭でimport { useState } from 'react'する
  2. const [keyword, setKeyword] = useState('')を宣言する
  3. 見出しの下に<input type="search">を置き、valueonChangeをつなぐ
  4. birds.filter(…)で絞りこんだshownBirdsを作り、mapする配列をそちらに変える
  5. 件数の表示と、0件のときのメッセージを足す
import { useState } from 'react'
import BirdCard from './components/BirdCard.jsx'
import { birds } from './data/birds.js'

function App() {
  const [keyword, setKeyword] = useState('')

  const shownBirds = birds.filter(
    (bird) => bird.name.includes(keyword) || bird.area.includes(keyword),
  )

  return (
    <div className="min-h-screen bg-sky-50 p-8">
      <h1 className="text-2xl font-bold text-sky-800">しまちゃんのことりずかん</h1>
      <p className="mt-1 text-sm text-slate-500">全{birds.length}羽をあつめよう</p>

      <input
        type="search"
        value={keyword}
        onChange={(event) => setKeyword(event.target.value)}
        placeholder="名前やすんでいる場所でさがす"
        className="mt-4 w-full max-w-sm rounded-full border border-sky-200 bg-white px-4 py-2 text-sm"
      />
      <p className="mt-2 text-sm text-slate-500">{shownBirds.length}羽みつかりました</p>

      {shownBirds.length === 0 ? (
        <p className="mt-6 text-slate-500">みつかりませんでした。</p>
      ) : (
        <div className="mt-6 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
          {shownBirds.map((bird) => (
            <BirdCard key={bird.id} bird={bird} />
          ))}
        </div>
      )}
    </div>
  )
}

export default App
検索ボックスに「まちなか」と入力されていて、スズメとハシブトガラスの2枚だけが表示されている。上には「2羽みつかりました」と出ている
「まちなか」で2羽。1文字打つたびに絞りこまれるのを確かめてください

「ざぶとん」のような当たらない言葉を打って、0件のメッセージが出ることも見ておいてください。

よくある質問

onClickに関数を渡すとき、括弧を付けてはいけないのはなぜですか?
括弧を付けるとその場で実行され、戻り値が渡ってしまうためです。渡したいのは「押されたときに実行する関数」そのものなので、括弧なしの関数名か、アロー関数で包んだ形を渡します。
制御コンポーネントとは何ですか?
入力欄の値をstateで持ち、valueとonChangeでReactと同期させる書き方のことです。画面に出ている文字とstateが常に一致するので、入力内容をそのまま検索や送信に使えます。
入力欄に文字が打てなくなりました
valueだけ指定してonChangeを書いていないと、値がstateに固定されて打てなくなります。valueとonChangeは必ずセットにしてください。
フォームの送信でページが再読みこみされてしまいます
submitイベントの既定の動作です。onSubmitの中でevent.preventDefault()を呼べば止まります。素のJavaScriptのときと同じ対処です。