2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

記事投稿キャンペーン 「2024年!初アウトプットをしよう」

ReactでSlotMachineを作る(Step2: Coin機能実装)

Last updated at Posted at 2024-01-01

はじめに

前回までの記事の続きです。(シリーズ化予定)

▪️実装Step
Step1:基本編
Step2:Coin機能実装編 ←いまここ
Step3: 仕様追加、リファクタリング実施
Step4:ワイルドカード、セブンカードの追加
Step5:ディレクトリ整理/リファクタリング

成果物

slot_step2.gif

追加仕様
・Coinは最初100枚。
・1回で3枚消費する。
・勝利すると10枚コインが追加される。

プロダクトコード

src/components/SlotMachine.tsx
import React, { useState } from "react";
import { Reel } from "./Reel";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";

const symbols = ["🍒", "🍋", "🍊", "🍇", "🍉"];

export const SlotMachine: React.FC = () => {
  const [coins, setCoins] = useState(100);
  const [reels, setReels] = useState(Array(3).fill(symbols[0]));

  const handleSpin = () => {
    const newReels = reels.map(
      () => symbols[Math.floor(Math.random() * symbols.length)]
    );
    setReels(newReels);
    setCoins((prev) => prev - 3);
    // 当たり判定
    if (new Set(newReels).size === 1) {
      setCoins(coins - 3 + 10); // 当たりならコインを追加
    }
  };

  const isWin = new Set(reels).size === 1;

  return (
    <Box sx={{ textAlign: "center", marginTop: 4 }}>
      <Box>Coinの枚数:{coins}</Box>
      <Box sx={{ display: "flex", justifyContent: "center", gap: 2 }}>
        {reels.map((symbol, index) => (
          <Reel key={index} symbol={symbol} />
        ))}
      </Box>
      <Button
        variant="contained"
        color="primary"
        style={{ marginTop: "16px" }}
        onClick={handleSpin}
        disabled={coins < 3}
      >
        スピン
      </Button>
      {isWin && <Box sx={{ marginTop: 2 }}>勝利!</Box>}
    </Box>
  );
};

テストコード

src/components/SlotMachine.test.tsx
import React from "react";
import { render, fireEvent, screen } from "@testing-library/react";
import { SlotMachine } from "./SlotMachine";

describe("SlotMachine コンポーネント", () => {
  test("初期レンダリングでスピンボタンが表示される", () => {
    render(<SlotMachine />);
    expect(screen.getByRole("button", { name: "スピン" })).toBeInTheDocument();
  });

  test("スピンボタンをクリックするとリールが回転する", () => {
    render(<SlotMachine />);
    const initialReels = screen
      .getAllByText("🍒")
      .map((reel) => reel.textContent);
    fireEvent.click(screen.getByRole("button", { name: "スピン" }));
    const updatedReels = screen
      .getAllByText(/🍒|🍋|🍊|🍇|🍉/)
      .map((reel) => reel.textContent);
    expect(updatedReels).not.toEqual(initialReels);
  });
});

describe("コインの消費", () => {
  test("スピンボタンをクリックするとコインが3枚消費される", () => {
    render(<SlotMachine />);
    fireEvent.click(screen.getByRole("button", { name: "スピン" }));
    expect(screen.getByText("Coinの枚数:97枚")).toBeInTheDocument();
  });
  test("コインが3枚以上あるときはスピンボタンをクリックできる", () => {
    render(<SlotMachine />);
    expect(screen.getByRole("button", { name: "スピン" })).toBeEnabled();
  });
  test("コインが0枚になるとスピンボタンは無効になる", () => {
    render(<SlotMachine />);
    const spinButton = screen.getByRole("button", {
      name: "スピン",
    }) as HTMLButtonElement;

    // コインを0枚にするまでボタンをクリックする
    while (!spinButton.disabled) {
      fireEvent.click(spinButton);
    }

    // ボタンが無効化されていることを検証
    expect(spinButton).toBeDisabled();
  });
});

実行結果
~/develop/react/react_slot_machine$ yarn test src/components/SlotMachine.test.tsx
 PASS  src/components/SlotMachine.test.tsx
  SlotMachine コンポーネント
    ✓ 初期レンダリングでスピンボタンが表示される (44 ms)
    ✓ スピンボタンをクリックするとリールが回転する (16 ms)
  コインの消費
    ✓ スピンボタンをクリックするとコインが3枚消費される (9 ms)
    ✓ コインが3枚以上あるときはスピンボタンをクリックできる (7 ms)
    ✓ コインが0枚になるとスピンボタンは無効になる (31 ms)

Test Suites: 1 passed, 1 total
Tests:       5 passed, 5 total
Snapshots:   0 total
Time:        0.449 s, estimated 1 s
Ran all test suites matching /src\/components\/SlotMachine.test.tsx/i.

まだ大した仕様を入れていないのに、段々と複雑化してきましたね。
コンポーネントに責務を持たせた関係で、コンポーネントの可読性・保守性が低下。
また、テストも検証がしにくいケースが増えてきました。もうちょっとすると開発がしんどくなり、開発生産性が爆下がりしてきます。どこかでリファクタリングを検討する段階に入りそうです😊

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?