
Reactサンプルアプリ開発 プラニングポーカー (4) 他のコンポーネントとの相互作用
次に、ボタン押下時のアクションを実装する。
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
コンポーネント。オープンされるとPlayerProps
のopen
プロパティが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>
)
これで、「オープン」ボタン押下によって各プレイヤーの手札の表示が変わるようになった。
現時点での動きは以下のような感じ(オープンしたカードは色が変わる)