- DAY 20:React基礎総合演習 前半 ― じゃんけんアプリで基礎を一気に使ってみる
- じゃんけんアプリのゴールをイメージする
- コンポーネント設計 ― 役割ごとに分けて考える
- useStateで「手」と「結果」を管理する
- Eventと条件分岐 ― 手を選んだときの処理を考える
- Propsで「ボタン」と「結果表示」を分ける
- コンポーネントを組み合わせて、じゃんけんアプリを完成させる
- DAY 20 前半のまとめ
- DAY 20:React基礎総合演習 中盤 ― じゃんけんアプリを「設計目線」で深掘りする
- コンポーネント分割をもう一度見直す
- useStateの置き場所と「単一の情報源」
- Propsで「データ」と「イベント」を渡す
- Eventと条件分岐を「読みやすく」書く
- Stateの初期値と「未選択状態」を意識する
- 中盤のまとめ用テンプレート
- DAY 20 中盤のまとめ
- DAY 20:React基礎総合演習 後半 ― じゃんけんアプリを「完成形」に育てる
- じゃんけんアプリに「勝ち数・負け数・あいこ数」を追加する
- 条件分岐+State更新で「勝ち数・負け数」を増やす
- ComponentとPropsで「スコア表示」を切り出す
- リセット機能を追加して「全体のState」をまとめて初期化する
- 完成版じゃんけんアプリテンプレート
- DAY 20:React基礎総合演習 後半のまとめ
DAY 20:React基礎総合演習 前半 ― じゃんけんアプリで基礎を一気に使ってみる
DAY 20の前半では、これまで学んできたReactの基礎をまとめて使うために、じゃんけんアプリを制作していきます。 このミニアプリの中で、次の要素をすべて使います。
- Component(コンポーネント)
- Props
useState- Event(イベント)
- 条件分岐
「手を選ぶ」「勝敗を判定する」「結果を表示する」という流れを通して、Reactの基本的な考え方を体で理解していくことを目標にします。
じゃんけんアプリのゴールをイメージする
どんな動きをするアプリかを言語化する
まず、完成形のイメージをはっきりさせます。
- ユーザーが「グー」「チョキ」「パー」のボタンから1つ選ぶ
- コンピューター側の手がランダムに決まる
- 「あなたの手」「コンピューターの手」「結果(勝ち・負け・あいこ)」を画面に表示する
- もう一度ボタンを押せば、何度でも勝負できる
この動きの中に、コンポーネント・Props・useState・イベント・条件分岐が自然に含まれるように設計していきます。
コンポーネント設計 ― 役割ごとに分けて考える
どんなコンポーネントに分けるか
じゃんけんアプリを、次のようなコンポーネントに分けて考えます。
JankenApp- アプリ全体をまとめる親コンポーネント
- State(手・結果)を持つ
HandButton- 「グー」「チョキ」「パー」のボタンを表すコンポーネント
- Propsで「どの手か」「クリック時の処理」を受け取る
ResultView- 「あなたの手」「コンピューターの手」「結果」を表示するコンポーネント
- Propsで表示する内容を受け取る
このように役割を分けることで、ComponentとPropsの関係を自然に使えるようになります。
useStateで「手」と「結果」を管理する
Stateとして持つべきものを決める
じゃんけんアプリでStateとして持つべきものは、次のようなものです。
- あなたの手 →
playerHand - コンピューターの手 →
cpuHand - 結果(勝ち・負け・あいこ) →
result
これらを、親コンポーネント JankenApp の中で useState を使って管理します。
import { useState } from "react";
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null); // "グー" など
const [cpuHand, setCpuHand] = useState(null); // "グー" など
const [result, setResult] = useState(""); // "勝ち" "負け" "あいこ"
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習</p>
</div>
);
}
export default JankenApp;
JSXここで、
playerHand→ ユーザーが選んだ手cpuHand→ コンピューター側の手result→ 勝敗の文字列
という役割を持たせています。
重要ポイント:
- 「画面に表示したい状態」は、必ずStateとして持つようにします。
- じゃんけんでは、「手」と「結果」がUIの中心となるStateです。
Eventと条件分岐 ― 手を選んだときの処理を考える
手を選んだときに何をするかをステップで整理する
ユーザーが「グー」「チョキ」「パー」のボタンをクリックしたときに、次の処理を行います。
- ユーザーの手(
playerHand)を更新する - コンピューターの手(
cpuHand)をランダムに決める - 2つの手を比較して、結果(
result)を条件分岐で決める
この一連の処理を、1つの関数 handlePlayerHandSelect にまとめます。
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null);
const [cpuHand, setCpuHand] = useState(null);
const [result, setResult] = useState("");
const hands = ["グー", "チョキ", "パー"];
const getRandomHand = () => {
const index = Math.floor(Math.random() * hands.length);
return hands[index];
};
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
};
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習</p>
</div>
);
}
JSXここで起きていることをステップで整理すると、
getRandomHand- 配列
handsからランダムに1つ選びます。
- 配列
judgeResult(player, cpu)- 条件分岐で「勝ち」「負け」「あいこ」を判定します。
handlePlayerHandSelect(selectedHand)- ユーザーの手を
selectedHandとして受け取り - コンピューターの手をランダムに決め
- 勝敗を判定し
- 3つのStateをまとめて更新します。
- ユーザーの手を
重要ポイント:
- Event(クリック)をきっかけに、
useStateでStateを更新し、条件分岐で結果を決める流れが、Reactの基本パターンです。 - 「処理の流れ」を関数の中にきれいに閉じ込めることで、コードが読みやすくなります。
Propsで「ボタン」と「結果表示」を分ける
HandButtonコンポーネントを作る
次に、「グー」「チョキ」「パー」のボタンをコンポーネントとして切り出します。 このコンポーネントは、Propsで次の情報を受け取ります。
label→ ボタンに表示する文字(例:”グー”)onClick→ クリックされたときに呼び出す関数
function HandButton({ label, onClick }) {
return (
<button onClick={() => onClick(label)}>
{label}
</button>
);
}
JSXここで、
label→ Propsとして渡された文字列onClick→ Propsとして渡されたイベントハンドラonClick={() => onClick(label)}→ ボタンがクリックされたときに、labelを引数として渡して呼び出す
という役割になっています。
ResultViewコンポーネントを作る
次に、「あなたの手」「コンピューターの手」「結果」を表示するコンポーネントを作ります。 このコンポーネントは、Propsで次の情報を受け取ります。
playerHandcpuHandresult
function ResultView({ playerHand, cpuHand, result }) {
return (
<div>
<h3>結果</h3>
<p>あなたの手:{playerHand ?? "未選択"}</p>
<p>コンピューターの手:{cpuHand ?? "未選択"}</p>
<p>勝敗:{result || "まだ勝負していません"}</p>
</div>
);
}
JSXここで、
playerHand ?? "未選択"→playerHandがnullのときは「未選択」と表示result || "まだ勝負していません"→resultが空文字のときは「まだ勝負していません」と表示
というように、Stateの初期状態も考慮した表示をしています。
コンポーネントを組み合わせて、じゃんけんアプリを完成させる
JankenAppにHandButtonとResultViewを組み込む
最後に、JankenApp の中で HandButton と ResultView を使って、じゃんけんアプリを完成させます。
import { useState } from "react";
function HandButton({ label, onClick }) {
return (
<button onClick={() => onClick(label)}>
{label}
</button>
);
}
function ResultView({ playerHand, cpuHand, result }) {
return (
<div>
<h3>結果</h3>
<p>あなたの手:{playerHand ?? "未選択"}</p>
<p>コンピューターの手:{cpuHand ?? "未選択"}</p>
<p>勝敗:{result || "まだ勝負していません"}</p>
</div>
);
}
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null);
const [cpuHand, setCpuHand] = useState(null);
const [result, setResult] = useState("");
const hands = ["グー", "チョキ", "パー"];
const getRandomHand = () => {
const index = Math.floor(Math.random() * hands.length);
return hands[index];
};
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
};
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習</p>
<div>
<h3>手を選んでください</h3>
{hands.map((hand) => (
<HandButton
key={hand}
label={hand}
onClick={handlePlayerHandSelect}
/>
))}
</div>
<ResultView
playerHand={playerHand}
cpuHand={cpuHand}
result={result}
/>
</div>
);
}
export default JankenApp;
JSXこの完成版の中には、
- Component:
JankenApp/HandButton/ResultView - Props:
label/onClick/playerHand/cpuHand/result useState:手と結果のState管理- Event:ボタンクリックで
handlePlayerHandSelectを呼び出す - 条件分岐:
judgeResultで勝敗を判定する
という、Reactの基礎要素がすべて詰まっています。
DAY 20 前半のまとめ
DAY 20の前半では、
- じゃんけんアプリという具体的な題材を通して、Component・Props・
useState・Event・条件分岐をまとめて使ったこと - 「手を選ぶ → コンピューターの手を決める → 勝敗を判定する → 結果を表示する」という一連の流れを、関数の中にきれいに閉じ込めたこと
- 親コンポーネントでStateを管理し、子コンポーネントにPropsで渡すというReactの基本構造を体感したこと
- Stateの初期値や未選択状態も考慮した表示(
nullや空文字への対応)を行い、実用的なUIに近づけたこと
をステップバイステップで確認しました。
このじゃんけんアプリをベースに、 「勝ち数・負け数をカウントする」「履歴を表示する」「アニメーションを付ける」など、 少しずつ機能を拡張していくことで、Reactの基礎がさらに深く定着していきます。
DAY 20:React基礎総合演習 中盤 ― じゃんけんアプリを「設計目線」で深掘りする
DAY 20の中盤では、前半で作ったじゃんけんアプリを少し発展させながら、 Component・Props・useState・Event・条件分岐を「設計目線」で整理していきます。
単に動くコードから一歩進んで、 「なぜこの分け方なのか」「どこにStateを置くべきなのか」「Propsは何を渡すべきなのか」 という視点を持つことで、Reactの基礎がぐっと実務に近づきます。
コンポーネント分割をもう一度見直す
親コンポーネントと子コンポーネントの役割
じゃんけんアプリでは、次のような役割分担が自然です。
- 親コンポーネント:
JankenApp- アプリ全体の状態(State)を管理する
- ロジック(勝敗判定・ランダムな手の決定)を持つ
- 子コンポーネントに必要な情報をPropsで渡す
- 子コンポーネント:
HandButton- 「グー」「チョキ」「パー」のボタンを表示する
- クリックされたときに、親から渡された関数を呼び出す
- 子コンポーネント:
ResultView- 「あなたの手」「コンピューターの手」「結果」を表示する
- 表示に必要な値をPropsで受け取る
この分け方のポイントは、 「Stateは親に集約し、子は“表示とイベントの入り口”に専念させる」ということです。
useStateの置き場所と「単一の情報源」
Stateはどこに置くべきか
じゃんけんアプリで使うStateは、次の3つでした。
playerHand(あなたの手)cpuHand(コンピューターの手)result(勝敗)
これらをすべて JankenApp に置く理由は、 「画面全体の状態を一箇所で管理するため」です。
もし、ResultView の中で独自にStateを持ってしまうと、
- 親と子で「どの値が正しいのか」が分かりにくくなる
- 表示とロジックがバラバラになり、修正が難しくなる
といった問題が出てきます。
Reactでは、 「その情報を一番よく知っているコンポーネントにStateを置く」 という考え方が大事です。
じゃんけんアプリの場合、
- 手を決めるロジック
- 勝敗を判定するロジック
を持っているのは JankenApp なので、 そこにStateを集約するのが自然です。
Propsで「データ」と「イベント」を渡す
HandButtonに渡すPropsを整理する
HandButton コンポーネントは、次のように定義していました。
function HandButton({ label, onClick }) {
return (
<button onClick={() => onClick(label)}>
{label}
</button>
);
}
JSXここで渡しているPropsは2つです。
label→ ボタンに表示する文字(”グー” / “チョキ” / “パー”)onClick→ ボタンがクリックされたときに呼び出す関数
この設計のポイントは、
- 「表示に必要なデータ(label)」
- 「ユーザー操作を親に伝えるためのイベントハンドラ(onClick)」
という2種類のPropsを分けて考えていることです。
親コンポーネント側では、次のように使っています。
{hands.map((hand) => (
<HandButton
key={hand}
label={hand}
onClick={handlePlayerHandSelect}
/>
))}
JSXここで、
label={hand}→"グー"などの文字列を渡すonClick={handlePlayerHandSelect}→ 「手が選ばれたときの処理」を渡す
という形になっています。
重要ポイント:
- Propsは、「子コンポーネントに必要な情報」と「子から親に伝えたいイベントの入り口」を渡すための仕組みです。
- 子は「何を表示するか」と「いつ親の関数を呼ぶか」だけを知っていればよく、ロジックの中身は親が持ちます。
Eventと条件分岐を「読みやすく」書く
勝敗判定ロジックを関数に切り出す
勝敗判定のロジックは、次のように書いていました。
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
JSXこの関数の役割は、
- 引数として「あなたの手」と「コンピューターの手」を受け取る
- 条件分岐で勝敗を判定する
- 結果の文字列(”勝ち” / “負け” / “あいこ”)を返す
というものです。
このようにロジックを関数に切り出すことで、
handlePlayerHandSelectの中身がスッキリする- 勝敗判定だけをテストしたり、修正したりしやすくなる
というメリットがあります。
イベントハンドラの中で「処理の流れ」を明確にする
handlePlayerHandSelect は、次のようになっていました。
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
};
JSXここでのポイントは、
- 「次の値」を変数として明示していること
nextPlayerHandnextCpuHandnextResult
- その「次の値」をStateに反映していること
setPlayerHand(nextPlayerHand);setCpuHand(nextCpuHand);setResult(nextResult);
このように書くことで、
- 「何が起きているか」が一行ずつ追いやすくなる
- State更新の順番や意味が読み手に伝わりやすくなる
という効果があります。
重要ポイント:
- Eventハンドラの中では、「処理の流れ」を変数名と関数名で分かりやすく表現することが大事です。
- 条件分岐やランダム処理は、できるだけ別関数に切り出して、イベントハンドラをシンプルに保つと読みやすくなります。
Stateの初期値と「未選択状態」を意識する
ResultViewでの表示ロジック
ResultView コンポーネントでは、次のように表示していました。
function ResultView({ playerHand, cpuHand, result }) {
return (
<div>
<h3>結果</h3>
<p>あなたの手:{playerHand ?? "未選択"}</p>
<p>コンピューターの手:{cpuHand ?? "未選択"}</p>
<p>勝敗:{result || "まだ勝負していません"}</p>
</div>
);
}
JSXここで使っているポイントは、
playerHand ?? "未選択"playerHandがnullのときは「未選択」と表示する
result || "まだ勝負していません"resultが空文字のときは「まだ勝負していません」と表示する
という、「初期状態」を考慮した表示です。
Reactでは、 「Stateがまだ決まっていない状態」も必ず存在します。
- 最初のレンダリングでは、ユーザーはまだ何も選んでいない
- そのときに何を表示するかを決めておく必要がある
この視点を持つことで、 「実際に使えるUI」に近づいていきます。
中盤のまとめ用テンプレート
最後に、DAY 20中盤の内容を反映した、読みやすさを意識したじゃんけんアプリのテンプレートを示します。
import { useState } from "react";
function HandButton({ label, onClick }) {
return (
<button onClick={() => onClick(label)}>
{label}
</button>
);
}
function ResultView({ playerHand, cpuHand, result }) {
return (
<div>
<h3>結果</h3>
<p>あなたの手:{playerHand ?? "未選択"}</p>
<p>コンピューターの手:{cpuHand ?? "未選択"}</p>
<p>勝敗:{result || "まだ勝負していません"}</p>
</div>
);
}
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null);
const [cpuHand, setCpuHand] = useState(null);
const [result, setResult] = useState("");
const hands = ["グー", "チョキ", "パー"];
const getRandomHand = () => {
const index = Math.floor(Math.random() * hands.length);
return hands[index];
};
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
};
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習</p>
<div>
<h3>手を選んでください</h3>
{hands.map((hand) => (
<HandButton
key={hand}
label={hand}
onClick={handlePlayerHandSelect}
/>
))}
</div>
<ResultView
playerHand={playerHand}
cpuHand={cpuHand}
result={result}
/>
</div>
);
}
export default JankenApp;
JSXDAY 20 中盤のまとめ
DAY 20の中盤では、
- 親コンポーネントにStateとロジックを集約し、子コンポーネントにはPropsで「データ」と「イベント」を渡す設計を確認したこと
useStateの置き場所を「その情報を一番よく知っているコンポーネント」にする、という考え方を身につけたこと- Eventと条件分岐を関数に切り出し、「処理の流れ」を読みやすく書く練習をしたこと
- 初期状態や未選択状態も含めてUIを設計し、実際に使えるじゃんけんアプリに近づけたこと
が大きなポイントになります。
後半では、このじゃんけんアプリに「勝ち数・負け数のカウンター」などを追加し、 State更新とコンポーネント設計をさらに一段深く練習していきます。
DAY 20:React基礎総合演習 後半 ― じゃんけんアプリを「完成形」に育てる
DAY 20の後半では、前半・中盤で作ってきたじゃんけんアプリを、 「基礎の総まとめ」として、少し“完成形”に近づけることを目標にします。
使うものは変わりません。
- Component
- Props
useState- Event
- 条件分岐
ですが、ここではそれぞれを「どう組み合わせると、読みやすくて拡張しやすいアプリになるか」という視点で深掘りしていきます。
じゃんけんアプリに「勝ち数・負け数・あいこ数」を追加する
何をStateとして増やすかを決める
まず、じゃんけんアプリを少しだけゲームらしくするために、
- 勝ち数 →
winCount - 負け数 →
loseCount - あいこ数 →
drawCount
を追加してみます。
これらは、「勝負の履歴」を表す重要な情報なので、JankenApp のStateとして持つのが自然です。
import { useState } from "react";
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null);
const [cpuHand, setCpuHand] = useState(null);
const [result, setResult] = useState("");
const [winCount, setWinCount] = useState(0);
const [loseCount, setLoseCount] = useState(0);
const [drawCount, setDrawCount] = useState(0);
// ...(後でロジックを追加します)
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習</p>
</div>
);
}
export default JankenApp;
JSX重要ポイント:
- 「画面全体の状態」を表すものは、親コンポーネントに集約します。
- 勝ち数・負け数・あいこ数は、じゃんけんアプリ全体の状態なので、
JankenAppに置くのが自然です。
条件分岐+State更新で「勝ち数・負け数」を増やす
勝敗判定の結果に応じてカウンターを更新する
すでにある judgeResult 関数を活かしつつ、 勝敗に応じてカウンターを更新するロジックを追加します。
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
JSXこの結果を使って、handlePlayerHandSelect の中でカウンターを更新します。
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
if (nextResult === "勝ち") {
setWinCount((prev) => prev + 1);
} else if (nextResult === "負け") {
setLoseCount((prev) => prev + 1);
} else if (nextResult === "あいこ") {
setDrawCount((prev) => prev + 1);
}
};
JSXここで起きていることをステップで整理すると、
- ユーザーが手を選ぶ →
handlePlayerHandSelectが呼ばれる nextPlayerHandとnextCpuHandを決めるjudgeResultで勝敗を判定し、nextResultを得る- 手と結果のStateを更新する
nextResultに応じて、勝ち数・負け数・あいこ数のカウンターを更新する
重要ポイント:
- 「前のStateをもとに1増やす」場面では、
setWinCount((prev) => prev + 1)のような関数型更新を使います。 - 条件分岐とState更新を組み合わせることで、「ロジックに意味を持たせたUI」が作れます。
ComponentとPropsで「スコア表示」を切り出す
ScoreBoardコンポーネントを作る
勝ち数・負け数・あいこ数を表示する部分を、 ScoreBoard というコンポーネントに切り出してみます。
function ScoreBoard({ winCount, loseCount, drawCount }) {
return (
<div>
<h3>スコア</h3>
<p>勝ち:{winCount}</p>
<p>負け:{loseCount}</p>
<p>あいこ:{drawCount}</p>
</div>
);
}
JSXここで、
winCount/loseCount/drawCountは、親コンポーネントからPropsとして渡される値です。ScoreBoardは「表示専用」のコンポーネントであり、Stateは持ちません。
親コンポーネント側では、次のように使います。
<ScoreBoard
winCount={winCount}
loseCount={loseCount}
drawCount={drawCount}
/>
JSX重要ポイント:
- 「表示だけを担当するコンポーネント」は、Propsでデータを受け取り、Stateを持たない形にするとシンプルになります。
- 親がStateを持ち、子が表示を担当する構造は、Reactの基本パターンです。
リセット機能を追加して「全体のState」をまとめて初期化する
じゃんけんアプリ全体をリセットするボタン
次に、「リセット」ボタンを追加して、 手・結果・スコアをすべて初期状態に戻す機能を作ります。
const handleResetGame = () => {
setPlayerHand(null);
setCpuHand(null);
setResult("");
setWinCount(0);
setLoseCount(0);
setDrawCount(0);
};
JSXこの関数を、ボタンのイベントハンドラとして使います。
<button onClick={handleResetGame}>リセット</button>
JSXここで起きていることをステップで整理すると、
- ユーザーが「リセット」ボタンをクリックする
handleResetGameが呼ばれる- すべてのStateを初期値に戻す
- 手 →
null - 結果 → 空文字
- スコア → 0
- 手 →
重要ポイント:
- 「前のStateは関係なく、特定の値に戻したい」場面では、
setX(初期値)のように直接値を渡します。 - リセット機能は、「アプリ全体のStateをどう設計しているか」を確認する良い練習になります。
完成版じゃんけんアプリテンプレート
ここまでの内容をすべてまとめた、 DAY 20後半の「完成版じゃんけんアプリ」のテンプレートを示します。
import { useState } from "react";
function HandButton({ label, onClick }) {
return (
<button onClick={() => onClick(label)}>
{label}
</button>
);
}
function ResultView({ playerHand, cpuHand, result }) {
return (
<div>
<h3>結果</h3>
<p>あなたの手:{playerHand ?? "未選択"}</p>
<p>コンピューターの手:{cpuHand ?? "未選択"}</p>
<p>勝敗:{result || "まだ勝負していません"}</p>
</div>
);
}
function ScoreBoard({ winCount, loseCount, drawCount }) {
return (
<div>
<h3>スコア</h3>
<p>勝ち:{winCount}</p>
<p>負け:{loseCount}</p>
<p>あいこ:{drawCount}</p>
</div>
);
}
function JankenApp() {
const [playerHand, setPlayerHand] = useState(null);
const [cpuHand, setCpuHand] = useState(null);
const [result, setResult] = useState("");
const [winCount, setWinCount] = useState(0);
const [loseCount, setLoseCount] = useState(0);
const [drawCount, setDrawCount] = useState(0);
const hands = ["グー", "チョキ", "パー"];
const getRandomHand = () => {
const index = Math.floor(Math.random() * hands.length);
return hands[index];
};
const judgeResult = (player, cpu) => {
if (player === cpu) {
return "あいこ";
}
if (
(player === "グー" && cpu === "チョキ") ||
(player === "チョキ" && cpu === "パー") ||
(player === "パー" && cpu === "グー")
) {
return "勝ち";
}
return "負け";
};
const handlePlayerHandSelect = (selectedHand) => {
const nextPlayerHand = selectedHand;
const nextCpuHand = getRandomHand();
const nextResult = judgeResult(nextPlayerHand, nextCpuHand);
setPlayerHand(nextPlayerHand);
setCpuHand(nextCpuHand);
setResult(nextResult);
if (nextResult === "勝ち") {
setWinCount((prev) => prev + 1);
} else if (nextResult === "負け") {
setLoseCount((prev) => prev + 1);
} else if (nextResult === "あいこ") {
setDrawCount((prev) => prev + 1);
}
};
const handleResetGame = () => {
setPlayerHand(null);
setCpuHand(null);
setResult("");
setWinCount(0);
setLoseCount(0);
setDrawCount(0);
};
return (
<div>
<h2>じゃんけんアプリ</h2>
<p>React基礎総合演習(DAY 20)</p>
<div>
<h3>手を選んでください</h3>
{hands.map((hand) => (
<HandButton
key={hand}
label={hand}
onClick={handlePlayerHandSelect}
/>
))}
</div>
<ResultView
playerHand={playerHand}
cpuHand={cpuHand}
result={result}
/>
<ScoreBoard
winCount={winCount}
loseCount={loseCount}
drawCount={drawCount}
/>
<button onClick={handleResetGame}>リセット</button>
</div>
);
}
export default JankenApp;
JSXDAY 20:React基礎総合演習 後半のまとめ
DAY 20の後半では、
- じゃんけんアプリに「勝ち数・負け数・あいこ数」のカウンターを追加し、State更新の応用を体験したこと
- 条件分岐とState更新を組み合わせて、「結果に応じてカウンターを増やす」ロジックを書いたこと
ScoreBoardコンポーネントを作り、Propsでスコアを渡すことで、表示専用コンポーネントの設計を確認したこと- リセット機能を通して、「アプリ全体のStateをどう初期化するか」を意識できるようになったこと
- Component・Props・
useState・Event・条件分岐を、1つのアプリの中で自然に組み合わせる感覚をつかんだこと
が大きなポイントになります。
読者のみなさんが、このじゃんけんアプリをさらに改造して、
- 勝率を表示する
- 何戦目かを表示する
- 手をアイコンや画像にする
など、少しずつ遊び心を足していくことで、Reactの基礎が「自分のもの」として定着していきます。

