投稿日:2025年1月30日
先日の記事 はバニラJSでカウントダウンアプリを作った内容でした。
バニラJSではソースコードが少々冗長に感じます。
Reactで作ってみました。
仕様は前回 とほぼ同じです。
現在の年月日、時分秒を表示してカウントします。
今回は「日時設定」の項目(input要素のdatetime-local
)を追加しており、ここで日時を指定します。
現在の日時(何も設定せずクリック)、または過去の日時を設定し「差を計算」をクリックするとエラーを表示しリセットします。
「差を計算」ボタンをクリックすると「現在時刻」と「設定した日時」までのカウントダウンが表示されます。
今回も「y年 dd日 hh:mm:ss」と表示するようにしてます。
コンポーネントは下図のようにしました。
❶CurrentTime.jsx 現在時刻を表示します。
❷DateTimeInput.jsx 設定したい日時を入力します。
❸CalculateButton.jsx 設定した日時を元に計算を開始するボタン。
❹ResetButton.jsx リセットボタン。
❺Countdown.jsx カウントダウンを表示します。
追加で上記の子コンポーネントを包含するTimer.jsxを作りました。
それぞれのソースコードです。
Timer.jsx
import React, { useState, useEffect } from "react";
import { CurrentTime } from "./CurrentTime";
import { Countdown } from "./Countdown";
export const Timer = () => {
const [time, setTime] = useState(new Date());
const [inputDateTime, setInputDateTime] = useState("");
const [difference, setDifference] = useState(null);
const [showDifference, setShowDifference] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
setTime(new Date());
if (showDifference && inputDateTime) {
const targetTime = new Date(inputDateTime);
const diff = Math.max(targetTime.getTime() - new Date().getTime(), 0);
setDifference(diff);
if (
targetTime.getFullYear() === new Date().getFullYear() &&
targetTime.getMonth() === new Date().getMonth() &&
targetTime.getDate() === new Date().getDate() &&
targetTime.getHours() === new Date().getHours() &&
targetTime.getMinutes() === new Date().getMinutes() &&
targetTime.getSeconds() === new Date().getSeconds()
) {
setTimeout(alertAndReset, 1000);
}
}
}, 1000);
return () => clearInterval(interval);
}, [showDifference, inputDateTime]);
const handleDateTimeChange = (event) => {
setInputDateTime(event.target.value);
};
const isValidDate = (dateString) => {
const date = new Date(dateString);
const [year, month, day] = dateString.split("T")[0].split("-");
return (
date.getFullYear() === parseInt(year) &&
date.getMonth() + 1 === parseInt(month) &&
date.getDate() === parseInt(day)
);
};
const calculateDifference = () => {
if (inputDateTime && isValidDate(inputDateTime)) {
const targetTime = new Date(inputDateTime);
const now = new Date();
if (targetTime <= now) {
alertAndResetFuture();
} else {
const diff = Math.max(targetTime.getTime() - now.getTime(), 0);
setDifference(diff);
setShowDifference(true);
}
} else {
alert("正しい日時を指定してください。");
}
};
const alertAndReset = () => {
alert("指定した日時になりました。");
resetTimer();
};
const alertAndResetFuture = () => {
alert("未来の日時を指定してください。");
resetTimer();
};
const resetTimer = () => {
setInputDateTime("");
setDifference(null);
setShowDifference(false);
};
return (
<div className="compWrap">
<div className="setWrap">
<CurrentTime time={time} />
<Countdown
inputDateTime={inputDateTime}
handleDateTimeChange={handleDateTimeChange}
calculateDifference={calculateDifference}
resetTimer={resetTimer}
showDifference={showDifference}
difference={difference}
/>
</div>
</div>
);
};
▲App.jsxの役割を持たせています。
日時のカウントや、その他の関数を定義し、それぞれの子コンポーネントに渡しています。
CurrentTime.jsx
import React from "react";
export const CurrentTime = ({ time }) => {
return (
<div>
<h1>カウントダウン</h1>
<p>現在時刻</p>
<p id="inputDate" className="inputElement">{`${time.getFullYear()}年${
time.getMonth() + 1
}月${time.getDate()}日 ${time.toLocaleTimeString()}`}</p>
</div>
);
};
▲現在の日時を表示するコンポーネント。
以下の子コンポーネントも含め、propsは({ time })
のように分割代入しています。
DateTimeInput.jsx
import React from "react";
export const DateTimeInput = ({ inputDateTime, handleDateTimeChange }) => {
return (
<>
<p>日時設定</p>
<input
id="setDate"
className="inputElement"
type="datetime-local"
step="1"
value={inputDateTime}
onChange={handleDateTimeChange}
/>
</>
);
};
▲日時を指定するコンポーネント。type="datetime-local" step="1"
のように書き、秒まで指定できるようにしています。
CalculateButton.jsx
import React from "react";
export const CalculateButton = ({ calculateDifference }) => {
return (
<p className="setBtn" onClick={calculateDifference}>
差を計算
</p>
);
};
▲計算コンポーネント。onClick
でTimer.jsxで定義した関数を実行させます。
ResetButton.jsx
import React from "react";
export const ResetButton = ({ resetTimer }) => {
return (
<p className="clearBtn" onClick={resetTimer}>
リセット
</p>
);
};
▲リセットボタンのコンポーネント。
Countdown.jsx
import React from "react";
import { DateTimeInput } from "./DateTimeInput";
import { CalculateButton } from "./CalculateButton";
import { ResetButton } from "./ResetButton";
export const Countdown = ({
inputDateTime,
handleDateTimeChange,
calculateDifference,
resetTimer,
showDifference,
difference,
}) => {
const formatTimeDifference = (diff) => {
if (diff === 0) {
return "0年0日0時間0分0秒";
}
const years = Math.floor(diff / (1000 * 60 * 60 * 24 * 365));
const days = Math.floor(
(diff % (1000 * 60 * 60 * 24 * 365)) / (1000 * 60 * 60 * 24)
);
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000 + 1); // 1秒追加
return `${years}年${days}日${hours}時間${minutes}分${seconds}秒`;
};
return (
<div>
<DateTimeInput
inputDateTime={inputDateTime}
handleDateTimeChange={handleDateTimeChange}
/>
<div className="btnsWrap">
<CalculateButton calculateDifference={calculateDifference} />
<ResetButton resetTimer={resetTimer} />
</div>
{showDifference && difference !== null && (
<p>
指定日時まで
<br />
<span className="inputElement">
{formatTimeDifference(difference)}
</span>
</p>
)}
</div>
);
};
▲カウントダウンを表示するコンポーネント。
受け取ったpropsを子コンポーネントに渡し結果を表示させます。
再度載せておきます。
現在の私のReactに対する知見では、手前味噌な言い方ですが、素晴らしい出来だと思います。
と言いつつ、実は上のソースコード(JSX)は全てMicrosoft Copilot に出力してもらったものです。
▲Copilotとのやりとり。
ちゃんと会話が成り立ちつつ、プロンプトに即した回答が得られます。
今更ながらですが、昨今のAIの完成度には驚愕します。
最近のWindows PC(OS)には標準で搭載されておりMicrosoft Store からもダウンロードすることができます。
Microsoft製なのでWindowsでしか使えないのかと思いましたが、ブラウザ でも使うことができます。
また、Mac版のEdgeでも使うことができます。
▲Mac版 Edge。右上にCopilotのアイコンがあります。
サイドバーとして使うことができます。
iOS版のCopilot もあります。
ここまで完成度が高いと、エンジニアの仕事もソースコードをガリガリ書くのではなく、AIにより良いプロンプトを与え、添削する仕事がメインになるのではないかと思います。
とは言っても、プログラムの知識がないとAIが書き出したものが正しいのか? の判断もできないので、引き続き勉強は必要です。
AIと共に仕事をすることが必然な時代になりました。
Chat GPTもそうですが、有料版はもっと性能が良いとのことでお勧めしてる人が多いです。
活用したいと思います。
最後まで読んでいただき、ありがとうございました。