Reactでカウントダウンタイマーを作る
from denoとTypeScriptでReactを使いたい
あくまで独立して動くクラスコンポーネントを作りたいだけなので、至極単純な作りにする
ソースコードの全文:ソースコード全文(Reactでカウントダウンタイマーを作る)
前提
code:timer.html
<!DOCTYPE html>
<head>
<title>timer.html</title>
<script type="module" src="timer.js"></script>
</head>
<body></body>
timer.jsは以下のコマンドで作成する
$ deno bundle timer.tsx timer.js
クラスコンポーネントを作る
code:timer.tsx
/// <reference no-default-lib="true" />
/// <reference lib="esnext" />
/// <reference lib="dom" />
import React from "https://esm.sh/react@18.2.0";
import { createRoot } from "https://esm.sh/react-dom@18.2.0/client";
interface CountdownTimerProps {
time: number;
}
interface CountdownTimerState {
time: number;
}
class CountdownTimer extends React.Component<
CountdownTimerProps, // propsの型
CountdownTimerState // stateの型
{
constructor(props: CountdownTimerProps) {
// 初期化
super(props);
this.state = { time: props.time };
}
render(): JSX.Element {
// 描画
return <input type="text" disabled value={this.state.time} />;
}
}
const root = createRoot(document.body);
root.render(<CountdownTimer time={10} />);
すっごくごちゃごちゃしているけれど、大事なのは以下の行
23. CountdownTimerというクラスを定義している
この時、React.Componentというクラスを継承し、propsとstate(React)の型も定義している
propsとは最初に指定される値のこと
DOMが描画されてから変化することはない
state(React)は現在の状態を示す値のこと
stateの値を変えると、render()も一緒に呼び出される
stateの値を変えるにはsetState()を使う必要がある(後述)
この中にカウント中の数値を保存して、カウントダウンの処理に使う
27. コンストラクターを定義して変数を初期化している
29. super(props)は書かなければいけないものとして覚えよう
30. state(React)の値を初期化している
32. render()を定義している
この関数の戻り値がブラウザ上で描画される
34. ブラウザ上に<input>を描画するように値を渡している
value属性に表示する数値を渡している
39. root(<body>)の直下にCountdownTimerを描画する
timeにnumber型の10を渡している
この値はCountdownTimerのprops.timeの中に格納される
実行すると、ブラウザ上に10が入ったテキストボックスが1つ出現する
https://gyazo.com/e71eb0ed049140bdfa4cf74f61eb1020
今は何も動かないが、これを毎秒1ずつ減らしていく処理をこれから書く
カウントダウンの処理を書く
クラスコンポーネント自身にカウントダウンの処理を書く
Root.Render()を何度も呼び出して再描画するという手段もなくはないが、カプセル化の観点や描画の軽量化の観点からあんまりよくないっぽい
code:timer.tsx
class CountdownTimer extends React.Component<
CountdownTimerProps, // propsの型
CountdownTimerState // stateの型
{
//(中略)
componentDidMount() {
// DOMが描画された後に最初に呼び出される処理
this.timerID = setInterval(
() => this.tick(), // tick関数を定期的に呼び出す
1000, // 1000ms(=1秒)に1回定期的に呼び出す
);
}
componentWillUnmount() {
// DOMが削除される時に呼び出される処理
clearInterval(this.timerID); // これ以上tick関数を呼び出さないようにする
}
tick() {
this.setState({
time: this.state.time - 1,
});
if (this.state.time == 0) console.log("カウントダウンが終了しました。");
}
//(中略)
}
// (後略)
行ごとの説明
6. componentDidMount()はDOMが描画された際に最初に呼び出される関数
1度しか呼び出されない(多分)
8. setInterval()で後述のtick関数を定期的に呼び出す
setInterval(関数オブジェクト, 呼び出す間隔(ミリ秒))
clearInterval()で止めない限り、tick関数は無限に呼び出される
14. componentWillUnmount()はDOMが削除される時に呼び出される関数
19. 1秒に1回呼び出される関数
ここにカウントダウンの処理を書く
20. this.setState()でstate(React)の値を変更する
stateに直接代入するのと違い、render()の呼び出しまで自動的にやってくれる
つまりstateの値がブラウザ上に反映される
23. カウントダウンの値が0になったら開発者コンソールにメッセージを出力する
実行するとカウントダウンが始まり、0になった瞬間にメッセージが出力される
https://gyazo.com/112d41faa3fb7a230a3e6c12592f2535
今回は最低限の処理しか書いていないので、0以降も数字が減り続けるぞ
気になった人はソースコードを改変してみよう
余談
カプセル化したクラスコンポーネントは複数個呼び出すことができる
code:TypeScriptReact(TSX)
root.render(
<div>
<CountdownTimer time={10} />
<CountdownTimer time={20} />
<CountdownTimer time={8} />
</div>,
);
https://gyazo.com/dcf72e1bf18f2ec863657879ab47728a
それぞれに違う値を渡すことができ、独立して動く
参考
state とライフサイクル – React
というか、このページの内容はほとんどここから来ている
ここにあった情報を元に、内容を削ったりTypeScriptの型などを追加したりしている