Reactサンプルアプリ開発 プラニングポーカー (4) 他のコンポーネントとの相互作用

Poker サンプル開発

次に、ボタン押下時のアクションを実装する。
Reactのコンポーネント構成は以下のようになっている。

コンポーネント構造

「オープン」押下時に、各プレイヤーの手札をオープンするようにする。(この操作はテーブルの親しかできない。本来は親であっても他のプレーヤーの手札は見えないのだが、現在はそこまで実装していない)。
さて、Reactでは基本的に親子間でしかやり取りができない。親から子へのプロパティ渡し、子から親へはプロパティのコールバックを経由したイベント送出だ。つまり、上の図でParentOperationsのボタンクリックイベントを、Playerコンポーネントに直接送出することはできない。

ではどうするか。
ParentOperationsコンポーネントは親に対してイベントを送出する。
イベントを受け取った親コンポーネントのTablePageは、自身のステートを更新すると、プロパティ経由で子に状態が伝わる(直接の子であるPlayersコンポーネントへ)。さらにその子である各Playerコンポーネントに状態が伝わるという流れ。

まずはParentOperationsから。

type ParentOperationsProps = {
  bidding: boolean;
  onOpen: () => void;
  onNewGame: () => void;
}

export const ParentOperations: FunctionComponent<ParentOperationsProps> = (props) => {

  const handleOpenButtonClick = () => {
    props.onOpen();
  }

  const handleNewGameButtonClick = () => {
    props.onNewGame();
  }

  return (
    <div className="parent-operations">
      <button type="button" name="open"
        disabled={!props.bidding} value="open"
        onClick={handleOpenButtonClick}>オープン</button>
      <button type="button" name="newgame" value="newgame"
        onClick={handleNewGameButtonClick}>次のゲーム</button>
    </div>
  )
}

21行目、buttonタグのonClick属性にハンドラの関数を指定しており、実際にボタンが押下されるとハンドラ(13−15行目)経由で親のコールバックが呼び出される。

親コンポーネント。onNewGameのハンドラを指定。

        <ParentOperations bidding={bidding}
          onOpen={handleOpen} onNewGame={handleNewGame} /

そのハンドラ関数。

  const handleNewGame = () => {
    const ps = players.slice();
    ps.forEach(p => {
      p.open = false;
      p.bid = "";
    });
    setPlayers(ps);
    setBidding(true);
  }

playersは自身のステート変数(配列)。Array.slice()でコピーした配列の各要素を更新し、値を初期化している。setPlayers()、setBidding()を呼び出してプレイヤーの状態と入札状態を更新している。

もちろんこれらのステート変数はuseStyles()を用いて定義している。

  const [bidding, setBidding] = useState(true)
  const [players, setPlayers] = useState(
    [
      { name: "織田信長", icon: 1, bid: "", open: false },
      { name: "丹羽長秀", icon: 2, bid: "", open: false },
      { name: "柴田勝家", icon: 3, bid: "", open: false },
      { name: "前田利家", icon: 4, bid: "", open: false },
      { name: "佐々成政", icon: 1, bid: "", open: false },
      { name: "明智光秀", icon: 2, bid: "", open: false },
    ]
  )

Playersコンポーネントは以下のような実装。プレイヤーの数だけ、Playerコンポーネントを配置している。

type PlayersProps = {
  players: PlayerProps[];
}

export const Players: FunctionComponent<PlayersProps> = (props) => {
  const players = props.players.map((player) => {
    return (
      <Player {...player} />
    );
  });
  return (
    <div className="players">
      {players}
    </div>
  )
}

Playerコンポーネント。オープンされるとPlayerPropsopenプロパティがtrueになるので、その場合はカードの表示を変えるためにCSSクラス名を追加するようにした(1行目、10行目)

  const playerCardOpen = props.open ? "player-card-open" : "";

  return (
    <div className="player">
      <div className="player-info">
        <div className={"player-icon-" + props.icon}>
        </div>
        <div className="player-name">{props.name}</div>
      </div>
      <div className={`player-card ${playerCardOpen}`} onClick={handleModalOpen}>
        <span className="point">{props.bid}</span>
      </div>
      <Modal isOpen={modalIsOpen} onRequestClose={handleModalClose}
        contentLabel="test" style={customStyles} >
        <Deck points={points} onCardSelected={handleCardSelect} />
      </Modal>
    </div>
  )

これで、「オープン」ボタン押下によって各プレイヤーの手札の表示が変わるようになった。

現時点での動きは以下のような感じ(オープンしたカードは色が変わる)

コメント

タイトルとURLをコピーしました