AsaDesign

React:三目並べ(コンポーネント、props、handleClick、state )

  • classNameを使ってスタイルをあてることができる
  • Reactコンポーネントを表示することができる
  • 親コンポーネントから子コンポーネントに値を渡すことができる(props)
  • インタラクティブなコンポーネントが作成できる(=クリック時に反応させるhandleClick)

値を渡すprops

親コンポーネントから子コンポーネントに値を渡す。今回はvalueという引数名を使用。

子コンポーネント

before

function Square() {
  return <button className="square">1</button>
}

after

function Square({value}) {
  return <button className="square">{value}</button>
}

親コンポーネント

before

<Square/>
<Square/>
<Square/>

after

<Square value="1" />
<Square value="2" />
<Square value="3" />

クリック時に反応させるhandleClick

子コンポーネントに記載。これで、ボタンをクリックするとログが出力されるようになる。

before

function Square({value}) {
  return <button className="square">{value}</button>
}

after

function Square({value}) {
  function handleClick() {
    console.log('クリック');
  }
  return <button className="square" onClick={handleClick}>{value}</button>
}

コンポーネントに記憶させるstate

  • useStateをインポート
  • state変数(現在の状態valueと更新用の関数setValue)、初期値useState(null)を設定
  • イベントハンドラ関数handleClickに、クリック時の動作を記述
import { useState } from "react";

function Square() {
  const [value, setValue] = useState(null);

  function handleClick() {
    setValue('X');
  }
  return <button className="square" onClick={handleClick}>{value}</button>
}

親コンポーネントはこの状態に戻す。

    <Square/>
    <Square/>
    <Square/>

今の状態では、四角をクリックするとXが増えていくだけ。

親にstate管理をさせる

「state の親コンポーネントへのリフトアップ(持ち上げ)」と言う。

  • 複数の子コンポーネントからデータを収集したい
  • あるいは 2 つの子コンポーネント同士で通信したい

と言う時は、親コンポーネントにstateを宣言する。handleClickも持たせる。

const [squares, setSquares] = useState(Array(9).fill(null));

function handleClick(i) {
  const nextSquares = squares.slice();
  console.log('nextSquares', nextSquares);
  nextSquares[i] = "X";
  setSquares(nextSquares);
}

Array(9).fill(null) は、9 個の要素を持つ配列を作成し、それぞれの要素を null に設定します。それを囲む useState() コールは、state 変数 squares を宣言し、初期値をこの配列にします。配列の各要素は、個々のマス目の値に対応します。後で盤面が埋まってくると、squares 配列は次のような見た目になる予定です:

['O', null, 'X', 'X', 'X', 'O', 'O', null, null]
    <Square value={squares[0]} onSquareClick={() => handleClick(0)}/>
    <Square value={squares[1]} onSquareClick={() => handleClick(1)}/>
    <Square value={squares[2]} onSquareClick={() => handleClick(2)}/>

◾︎アロー関数について

ユーザがクリックするまでhandleClick を呼び出したくないので、handleClick(0) を呼び出す関数がほしい。
新しい () => 構文に注目してください。この () => handleClick(0)アロー関数と呼ばれる、関数を短く定義する方法です。マス目がクリックされると、アロー (=>) の後のコードが実行され、handleClick(0) が呼び出されます。

子コンポーネントはこれだけになる。

function Square({value, onSquareClick}) {
  return (
    <button className="square" onClick={onSquareClick}>
      {value}
    </button>
  );
}
  1. ボタンを押すと、親から子へ管理番号(value)とイベント実行の権利(onSquareClick)が渡され、子はイベントを発火する。イベント内容(handleClick)は親コンポーネント内に記述されている。
  2. 【親】handleClick は管理番号を使って、squares 配列の要素を null から X に更新する。(更新関数を使用)
  3. 【親】状態stateが更新されたので、親と子が全て再レンダーされる。これで、valueがnullからXになる。

イミュータビリティ(不変性、直接更新しないこと)の大切さ

handleClick 内で既存の squares 配列を直接変更するのではなく、.slice() を使ってコピーを作成していたのはイミュータビリティの保持のため。

元のデータの書き換えを行わないことで、いくつかの利点を得ることができる。

  • 操作の履歴を見たり、操作を巻き戻す機能が実装できる。
  • コンポーネントのデータが変更されたかどうかを比較できる → 再レンダー不要な子コンポーネントを識別し、レンダーさせない。