0
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?

【Reactアプリ100本ノック】04 Timer

Posted at

はじめに

ゆかっしゅです。
2021年に事務職からデザイナー/コーダーにジョブチェンジをし、現在はフロントエンドエンジニアにスキルアップするべく、モダンフロントエンドを学習しています。

ReactやTypeScriptの基礎学習は終了したのに、なかなか0から自分だけでアプリを作れないので手を動かすべく「Reactアプリ100本ノック」 に挑戦してみようと思います。

Reactアプリ100本ノックルール

  • 主要なライブラリやフレームワークはReactである必要がありますが、その他のツールやライブラリ(例: Redux, Next.js, Styled Componentsなど)を組み合わせて使用することは自由
  • TypeScriptを利用する
  • 要件をみたせばデザインなどは自由
  • なるべくChatGPTなどのAIツールに頼らない(自分独自ルール)

04. Timer

スクリーンショット 2025-04-28 113605.png

問題

タイマーアプリを作成します

目的

タイマーアプリでは、Reactの根幹をなす状態管理、ライフサイクルメソッド、そしてイベント処理の実践を通じて、Reactの基礎を固めます

またsetTimeoutなど指定した時間に何かを実行するという関数を学んでいきます

達成条件

1.ユーザーは分と秒を入力してタイマーを設定できる。
2.スタートボタンを押すとカウントダウンが開始される。
3.時間が0になったら、ユーザーに通知される (効果音が鳴る)
4.一時停止ボタンを押すとカウントダウンが停止し、再開ボタンでカウントダウンが再開される。
5.リセットボタンでタイマーが設定時間に戻る。
6.無効な時間(例:負の時間、非数値、60分以上の値)が入力された場合、エラーメッセージが表示される。

実際に解いてみた

利用技術

React(19.0.0)
TypeScript(5.0)
Next.js(15.3.1)
Tailwindcss(4.0)
jotai(2.12.3)
Vercel

解答時間:5.5時間

すごく時間がかかってしまいました....。
初めてタイマーアプリ作ったのですが、まだuseEffectをしっかり落とし込めてないなぁということを再確認できました。
また、状態管理に関しては教えていただいた「Jotai」を早速使ってみました!
調べながらやったのでさらに時間がかかりました。
また、レンダリング最適化もやってみました!

リンク

Jotaiを使用してみて

RecoilがReactv19に対応していないことがわかり、代わりの状態管理を探していたところ
Jotaiを教えてもらったので使ってみました。
とても使いやすいです!!!!!
今後はこのJotaiを使用して状態管理をしてみようと思います。

一つだけつまずいた点

input.tsx

"use client";

import { useAtom } from "jotai";
import { PrimaryButton } from "./components/primaryButton";
import { minutesAtom, secondsAtom, startTimer, timerActive } from "./atom";
import { useState } from "react";

export default function Input() {
  const [minutes, setMinutes] = useAtom(minutesAtom);
  const [seconds, setSeconds] = useAtom(secondsAtom);
  const [, setActive] = useAtom(timerActive);
  const [, setStart] = useAtom(startTimer);

  const [error, setError] = useState("");

  const handleStart = () => {
    if (minutes < 0 || minutes > 99) {
      setError("分は0〜99の範囲で入力してください");
      return;
    }
    if (seconds < 0 || seconds > 59 || seconds === 0) {
      setError("秒は0〜59の範囲で入力してください");
      return;
    }

    setError("正しい数値(時間)を入力してください");
    setActive(true);
    setStart(true);
  };

  const handleMinutesChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = Number(e.target.value);
    if (value >= 0 && value <= 99) {
      setMinutes(value);
    }
  };

  const handleSecondsChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const value = Number(e.target.value);
    if (value >= 0 && value <= 59) {
      setSeconds(value);
    }
  };

  return (
    <div className="flex flex-col gap-25 items-center">
      <div className="flex gap-2 font-bold text-3xl">
        <div>
          <input
            type="number"
            min={0}
            max={99}
            value={minutes}
            onChange={handleMinutesChange}
            className="w-16 border-solid border-gray-300 border-2 rounded-md p-1"
          />
          
        </div>
        <div>
          <input
            type="number"
            min={0}
            max={59}
            value={seconds}
            onChange={handleSecondsChange}
            className="w-16 border-solid border-gray-300 border-2 rounded-md p-1"
          />
          
        </div>
      </div>
      {error && <p className="text-red-500 mt-2">{error}</p>}
      <PrimaryButton handleStart={handleStart}>スタート</PrimaryButton>
    </div>
  );
}

const [active, setActive] = useAtom(timerActive);
値は使わず更新関数だけ使用したい時、最初上記のように記載していたのですが
ESLintで「これ使ってないよ!」っというエラーが出てしまいどのように書けばいいのかわからずちょっと躓きました。

結果は下記のように書くとエラーが出ずスムーズに実装できました。
const [, setActive] = useAtom(timerActive);

React developer toolsと再レンダリング

今回たくさんのStateを作成したので、「無駄な再レンダリング起きてそうだなぁ。そもそも再レンダリングしてるところ簡単に調べられるツールないかなぁ。」と思って探したところ、React developer toolsを見つけました。
もともとChromにはインストールしてあったんですが使いどころがわからず眠っていたこのツール。
ざっくりとした使い方を下記に記載します。

React developer tools

検証モードを開いて、コンソールの横にある>>をクリックしてProfilerをクリックします。
(Reactで作られてるアプリやサイトでないとこのボタンは表示されないみたいです)
スクリーンショット 2025-04-28 142323.png

そして歯車ボタンを押して「Highlight updates when components render.」のところにチェックをいれます。
スクリーンショット 2025-04-28 142143.png

そしてアプリを動かしてみると...

スクリーンショット 2025-04-28 141756.png

再レンダリングしている箇所が緑色の四角に囲われて表示されます。
画像の場合はコンポーネントとして作成されたSecondaryButtonStopButtonが1秒減るごとに再レンダリングされていました。
ここでは秒数のみ再レンダリングして欲しく、ボタンは再レンダリングの必要がないのでmemo化します。
memo化した後の画像はコチラ。

スクリーンショット 2025-04-28 142649.png

ボタンが再レンダリングされなくなりました!

終わりに

とても時間がかかってしまいましたが、useEffectを始めJotaiの使い方やパフォーマンスチューニングについて学べてとてもいい学習になりました。

0
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
0
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?