TL;DR
このページでは、「タイマーアプリを作る」方法について解説します。一言で言うと「タイマーアプリ」とは、特定の時間を設定し、その時間が経過したら通知するアプリのことです。
開発環境 | バージョン |
---|---|
Next.js | 13.4.4 |
TypeScript | 5.0.4 |
Emotion | 11.11.0 |
React | 18.2.0 |
「タイマーアプリ」コンポーネント
「タイマーアプリ」は、指定した時間が経過したら通知を出すというシンプルなアプリケーションです。ただし、そのシンプルさがユーザーにとって使いやすさを生むため、実装面では細部まで注意を払う必要があります。最も基本的な要素としては「時間の設定」「時間のカウントダウン」「時間経過の通知」の 3 つが挙げられます。
以下に、一番基本的なタイマーアプリの機能をもつコンポーネントを見ていきましょう。
// ファイル名: components/BasicTimer.tsx
// ReactとReactのHooksであるuseStateをインポートしています
import React, { useState } from 'react'
// プロパティの型定義を行います。ここでは、初期カウントをnumber型で受け取るようにしています
type Props = {
initialCount: number
}
// Functional Componentを定義します。Props型の初期カウントを受け取ります
const BasicTimer = ({ initialCount }: Props) => {
// カウントの状態とその更新関数を定義します。初期値はPropsから受け取った初期カウントです
const [count, setCount] = useState(initialCount)
// tick関数を定義します。カウントが0より大きいとき、カウントを1減らします
const tick = () => {
if (count > 0) setCount((prevCount) => prevCount - 1)
}
// useEffect Hookを使って、コンポーネントのレンダリング後に実行する処理を設定します
React.useEffect(() => {
// 1秒ごとにtick関数を実行するタイマーを設定します
const timerId = setInterval(() => {
tick()
}, 1000)
// クリーンアップ関数を定義します。この関数はコンポーネントがアンマウントされたとき、
// または依存配列が更新されたときに呼ばれます。
// ここではタイマーをクリアします
return () => clearInterval(timerId)
}, [count]) // 依存配列にカウントを指定します。
// これにより、カウントが変更されるたびにエフェクトが実行されます
// カウントを表示するdiv要素をレンダリングします
return <div>{count}</div>
}
// BasicTimerコンポーネントをエクスポートします。
// 他のファイルからimportして使うことができます
export default BasicTimer
このコンポーネントでは、React のuseState
とuseEffect
を使って、カウントダウンの機能を実装しています。useState
は状態管理のためのもので、ここではタイマーのカウントを管理しています。useEffect
は副作用を扱うためのもので、ここでは時間が経過するたびにカウントを減らしています。
このコードをより詳しく見ていきましょう。
まず、useState
を使ってカウントの初期値を設定します。次にtick
という関数を定義し、カウントが 0 より大きい場合はカウントを 1 減らすようにします。そして、useEffect
を使って 1 秒ごとにtick
関数を実行します。これにより、カウントダウンの機能が実現できます。また、useEffect
のクリーンアップ関数でタイマーをクリアして、メモリリークを防ぎます。
以上が、タイマーアプリの基本的な仕組みとなります。しかし、現実のタイマーアプリではさらに多くの機能が必要となるでしょう。例えば、時間の設定、カウントダウンの一時停止、再開、リセットなどの機能が求められます。次のセクションでは、これらの機能を含むより実用的なタイマーアプリを Next.js と TypeScript で作成していきます。
Next.js で、タイマーアプリを実装
さて、次にタイマーアプリの実装に移ります。ここでは、基本的なタイマー機能に加えて、「時間の設定」「カウントダウンの一時停止」「再開」「リセット」の各機能を実装していきます。
まずは、タイマーコンポーネントの全体のコードから見ていきましょう。
// ファイル名: components/TimerApp.tsx
// ReactとReactのHooksであるuseStateをインポートします
import React, { useState } from 'react'
// プロパティの型定義を行います。初期カウントをnumber型で受け取ることができます
type Props = {
initialCount: number
}
// TimerAppというFunctional Componentを定義します。Props型のinitialCountを受け取ります
const TimerApp = ({ initialCount }: Props) => {
// カウントとその更新関数をuseState Hookで定義します。
// 初期値はPropsから受け取った初期カウントです
const [count, setCount] = useState(initialCount)
// タイマーが動いているかどうかの状態とその更新関数をuseState Hookで定義します。
// 初期値はfalseです
const [isRunning, setIsRunning] = useState(false)
// タイマーを開始する関数を定義します。isRunningの状態をtrueに更新します
const start = () => setIsRunning(true)
// タイマーを一時停止する関数を定義します。isRunningの状態をfalseに更新します
const pause = () => setIsRunning(false)
// タイマーをリセットする関数を定義します。カウントを初期カウントに戻し、
// isRunningの状態をfalseに更新します
const reset = () => {
setCount(initialCount)
setIsRunning(false)
}
// カウントダウンを実行する関数を定義します。
// カウントが0より大きいとき、カウントを1減らします
const tick = () => {
if (count > 0) setCount((prevCount) => prevCount - 1)
}
// useEffect Hookを使って、コンポーネントのレンダリング後に実行する処理を設定します
React.useEffect(() => {
let timerId: NodeJS.Timeout | null = null
// タイマーが動いていて、カウントが0より大きい場合、
// 1秒ごとにtick関数を実行するタイマーを設定します
if (isRunning && count > 0) {
timerId = setInterval(() => {
tick()
}, 1000)
}
// クリーンアップ関数を定義します。
// この関数はコンポーネントがアンマウントされたとき、
// または依存配列が更新されたときに呼ばれます。ここではタイマーをクリアします
return () => {
if (timerId) clearInterval(timerId)
}
}, [isRunning, count])
// 依存配列にisRunningとカウントを指定します。
// これにより、いずれかが変更されるたびにエフェクトが実行されます
// カウントと操作ボタンを表示するdiv要素をレンダリングします
return (
<div>
<div>{count}</div> {/* 現在のカウントを表示します */}
<button onClick={start}>Start</button> {/* Startボタン。クリックするとstart関数が実行されます */}
<button onClick={pause}>Pause</button> {/* Pauseボタン。クリックするとpause関数が実行されます */}
<button onClick={reset}>Reset</button> {/* Resetボタン。クリックするとreset関数が実行されます */}
</div>
)
}
// TimerAppコンポーネントをエクスポートします。他のファイルからimportして使うことができます
export default TimerApp
タイマーの制御
このコードでは、まずuseState
を使ってcount
とisRunning
の 2 つの状態を管理しています。
次に、タイマーの一時停止、再開、リセットを行うためのstart
、pause
、reset
関数を定義しています。start
関数はタイマーを開始するために、isRunning
をtrue
に設定します。pause
関数はタイマーを一時停止するために、isRunning
をfalse
に設定します。そして、reset
関数はカウントを初期値に戻し、タイマーを停止するために、isRunning
をfalse
に設定します。
カウントダウン処理
カウントダウンの処理は、tick
関数とuseEffect
フックを使って行います。tick
関数はカウントが 0 より大きい場合にカウントを 1 減らします。そして、useEffect
フックを使って、タイマーが動作していてカウントが 0 より大きい場合に、1 秒ごとにtick
関数を実行します。
コンポーネントのレンダリング
最後に、タイマーの値と、タイマーの開始、一時停止、リセットを行うボタンをレンダリングします。ボタンはそれぞれonClick
プロパティを使って、対応する関数を呼び出します。
以上がタイマーアプリの実装となります。ここまでで、タイマーアプリの基本的な機能を持つコンポーネントが完成しました。次にこのコンポーネントを呼び出す方法を見ていきましょう。
タイマーアプリの呼び出し方法
次に、作成したTimerApp
コンポーネントを呼び出す方法を見ていきます。TimerApp
コンポーネントはinitialCount
というプロパティを受け取ります。このプロパティはタイマーの初期値として使われます。
// ファイル名: pages/index.tsx
import React from 'react'
import TimerApp from '../components/TimerApp'
const IndexPage = () => {
return (
<div>
<h1>Timer App</h1>
<TimerApp initialCount={60} />
</div>
)
}
export default IndexPage
以上が、タイマーアプリの呼び出し方になります。ここでは、TimerApp
コンポーネントに60
という初期値を与え、1 分のタイマーを作成しています。
以上で、Next.js と TypeScript を使ったタイマーアプリの作成は完了です。これらのコードを活用して、自分だけのタイマーアプリを作成してみてください。