1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【react+typescript】信号機アプリを作っていろんな型と状態管理に慣れよう

Posted at

作るもの

信号機のように3つのライト(赤・黄・緑)が1秒ごとに切り替わるUIをReact + TypeScriptで実装します。

こんな感じ

画面録画 2025-03-26 234710.gif


概要

  • ReactのuseEffectuseStateを使って1秒ごとに状態を更新
  • TypeScriptの型定義で安全に状態を扱う
  • setTimeoutで1秒後に状態を切り替え、再レンダリングを誘発
  • clearTimeoutで不要なタイマーをクリーンアップ

コードの全体像

import { NextPage } from 'next';
import { useEffect, useState } from 'react';

const Page: NextPage = () => {
  const CYCLE = { red: 'green', green: 'yellow', yellow: 'red' } as const;
  const [light, setlight] = useState<keyof typeof CYCLE>('red');

  useEffect(() => {
    const timeID = setTimeout(() => {
      const nextLight = CYCLE[light];
      setlight(nextLight);
      console.log('再レンダリング');
    }, 1000);

    return () => clearTimeout(timeID);
  }, [light]);

  return (
    <div className="mx-auto max-w-4xl">
      <div className="mt-10 flex justify-center gap-x-8">
        <div className={`size-12 rounded-full ${light === 'red' ? 'bg-red-700' : 'bg-gray-700'}`} />
        <div className={`size-12 rounded-full ${light === 'green' ? 'bg-green-700' : 'bg-gray-700'}`} />
        <div className={`size-12 rounded-full ${light === 'yellow' ? 'bg-yellow-500' : 'bg-gray-700'}`} />
      </div>
    </div>
  );
};

export default Page;

コードを日本語化して見てみる

  • CYCLE:信号の色の遷移を定義した定数オブジェクト
  • useState:現在の信号の色(light)を保持。初期値は'red'
  • useEffectlightが変わるたびに1秒後の色に更新するタイマーをセット
  • setTimeout:1秒後にlightを次の色に切り替え
  • clearTimeout:次のレンダリング前に前のタイマーを解除し、バグやリークを防ぐ
  • return:JSXで3つのライトを表現。lightの状態に応じて色を変える

各コードの詳細

型定義の2つについて

const CYCLE = { red: 'green', green: 'yellow', yellow: 'red' } as const;
  • as const:オブジェクトをリテラル型として扱い、値が固定されるようにする。
  • 結果的にCYCLEの値が"green"などの文字列リテラルとして認識される。
const [light, setlight] = useState<keyof typeof CYCLE>('red');
  • typeof CYCLE:オブジェクトCYCLEの型を取得
  • keyof:その型のキー('red' | 'green' | 'yellow')を取り出す
  • lightはこの3つのどれかしか入れられなくなり、型安全になる

useEffect内のロジック

  • setTimeout:1秒後に次の色へ切り替える関数を実行
  • CYCLE[light]:現在の色に応じて次の色を取得
  • setlight(nextLight):状態を更新して再レンダリングを誘発
  • clearTimeout(timeID):レンダリング前に前のタイマーをキャンセル

おまけ:カスタムフック化

自分は以下のような Next.js + TypeScript 環境を使っています。
hooks ディレクトリに切り出すことで再利用性が上がります。

// src/hooks/useTrafficLight.ts
export const useTrafficLight = () => {
  const CYCLE = { red: 'green', green: 'yellow', yellow: 'red' } as const;
  const [light, setlight] = useState<keyof typeof CYCLE>('red');

  useEffect(() => {
    const timer = setTimeout(() => {
      setlight(CYCLE[light]);
    }, 1000);
    return () => clearTimeout(timer);
  }, [light]);

  return { light };
};
// src/pages/index.tsx
import { useTrafficLight } from '@/hooks/useTrafficLight';

const Page = () => {
  const { light } = useTrafficLight();

  return (
    <div className="mx-auto max-w-4xl">
      <div className="mt-10 flex justify-center gap-x-8">
        <div className={`size-12 rounded-full ${light === 'red' ? 'bg-red-700' : 'bg-gray-700'}`} />
        <div className={`size-12 rounded-full ${light === 'green' ? 'bg-green-700' : 'bg-gray-700'}`} />
        <div className={`size-12 rounded-full ${light === 'yellow' ? 'bg-yellow-500' : 'bg-gray-700'}`} />
      </div>
    </div>
  );
};

export default Page;

疑問点などありましたらコメントいただけると幸いです🙇

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?