Reactサンプルアプリ開発 プラニングポーカー (14) カスタムフックを使った処理の再利用

Poker サンプル開発

今回は、プレイヤーの参加や新しいゲーム開始などのゲーム状況の更新を画面上に表示するようにする。(下図、プレイヤーの下に表示されているテキスト)

Commentaryというコンポーネントを新たに作成してTablePageコンポーネント内に配置(14行目)。

  return (
    <>
      <header className="header">
        <span className="table-name">{props.tableName}</span>
      </header>
      <main>
        {isParent && <ParentOperations bidding={bidding}
          onOpen={handleOpen} onNewGame={handleNewGame} />}
        <GameResults show={showsResults} bids={bids} />
        <Players players={
          players.map((p, idx) => Object.assign(p,
            { onBidChange: (bid: string) => handleBidChange(bid, idx) }))
        } />
        <Commentary logs={logs} />
      </main>
    </>
  )

Commentaryコンポーネントはプロパティで渡された文字列の配列をリスト表示する単純なコンポーネント。

import React, { FunctionComponent } from "react";

export type CommentaryProps = {
  logs: string[];
}

export const Commentary: FunctionComponent<CommentaryProps> = (props) => {
  const items = props.logs.map((log) => {
    return (
      <li>{log}</li>
    );
  });

  return (
    <div className="commentary">
      <ul>
        {items}
      </ul>
    </div>
  );
}

さて、プレイヤー参加などの記録したい状況は4つほどあるのだが、useStateで配列更新用の関数setLogsを取得した場合、現在のlogsの先頭に文字列を追加した新しい配列を生成してsetLogsを呼び出すという処理を4箇所に同様に書かねばならない。

まぁ普通に関数化して呼び出せば済む話なのだが、今回は勉強のためにカスタムフックを使ってみた。

// カスタムフック
const useLogs = () => {
  const [logs, setLogs] = useState<string[]>([]);
  const addLog = useCallback((log: string) => {
    const newLogs = [log].concat(logs);
    setLogs(newLogs);
  }, [logs]);
  return [logs, addLog] as const;
}

カスタムフックは関数コンポーネントの外側に定義。
カスタムフックの関数内のトップレベルではuseStateuseEffectを利用可能である。ここではuseStateを利用して文字列配列とその更新関数を作成している(3行目)。
4-7行目で、コールバック関数を定義。受け取った文字列を配列の先頭に追加し、setLogsで更新をかけている。
そして、logsaddLogsを呼び出し元に公開するため、タプル形式でreturnしている(8行目)。TypeScriptの場合、タプルで返す場合はas constを付けるのがコツ。

呼び出し側の記述は以下のようになる。

export const TablePage: FunctionComponent<TableProps> = (props) => {
  const [bidding, setBidding] = useState(true);
  const [showsResults, setShowsResults] = useState(false);
  const [players, setPlayers] = useState<PlayerProps[]>([]);
  const [logs, addLog] = useLogs();

useLogs()を呼び出してlogsaddLogを得る(5行目)。

  useEffect(() => {
    socket.on("joined", (data: any) => {
      console.log(`${data.playerName} has joined (ID=${data.playerId})`);
      const joined = mapPlayer(data.playerName, false, data.playerId);
      const updatedPlayers = players.slice();
      updatedPlayers.push(joined);
      setPlayers(updatedPlayers);
      addLog(`${data.playerName}さんが参加しました`);
    });
    return () => {
      socket.off("joined");
    }
  }, [players, logs])

addLog関数を呼び出して状況ログの1行追加している(8行目)。

今回は、簡単な例ではあるがカスタムフックの使い方を学んだ。

コメント

  1. […] […]

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