投稿日:2021年5月22日
仕事でjQueryをよく使います。
次のステップとして、およそ半年前からReactの勉強を始めました。
▲この本で勉強してます。
掌田津耶乃さま 著 『React.js & Next.js超入門』
「超入門」と言うことだけあって、私のような初心者にも分かりやすく書かれています。
写経しながら一通り読み終えたので、試しに何か書いてみようと思います。
先日のカレンダー機能 をReactで書いてみました。
▼全体のソースコードです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>my calendar</title>
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
<style>
#contentsWrap {
background-color: #FAF0E6;
padding: .5em;
}
</style>
</head>
<body>
<div id="contentsWrap">
<input type="date" id="calendar">
<div id="root"></div> <!-- ここがReactの対象 -->
</div>
<script type="text/babel">
////////////////////
// ▼▼▼ スタイル
////////////////////
const btnWrap = {
display: "flex",
}
const btn = {
fontSize: ".8em",
backgroundColor: "#bbb",
padding: ".25em",
width: "4em",
textAlign: "center",
borderRadius: "5px",
marginRight: ".5em",
cursor: "pointer",
boxShadow: "1px 1px 2px #888",
transition: ".3s",
}
////////////////////
// ▼▼▼ 変数
////////////////////
let getDate = new Date(), //Dateを取得
y = getDate.getFullYear(), //年
month = getDate.getMonth() + 1, //月
day = getDate.getDate(); //日
let calendar = document.getElementById('calendar');
calendar.value = y + "-" + zeroPad(month) + "-" + zeroPad(day);//今日の日付
let timer = 1000; //経過速度
let r = 1; //回転方向
let pause = true; //初期状態は停止している
////////////////////
// ▼▼▼ 関数
////////////////////
function zeroPad(zeroNum) { //ゼロパッド取得関数
var zeroNum = ('00' + zeroNum).slice(-2);
return zeroNum;
}
// ▼ カレンダー変更
let calenderChange = (febDay) => {
calendar.addEventListener('change', function () {
if (pause == false) {
pause = true;
clearTimeout(this.countTimer); //タイマー一時停止
stopBtn.textContent = '再開';
}
let changeCalVal = calendar.value; //変更したカレンダーの値を取得
let changeCalDate = new Date(changeCalVal);
let changeCalYear = changeCalDate.getFullYear(); //年を取得
let changeCalMonth = changeCalDate.getMonth(); //月を取得
let changeCalDays = changeCalDate.getDate(); //日を取得
// ▼ うるう年のロジック
!(changeCalYear % 4 == 0 && changeCalYear % 100 != 0 || changeCalYear % 400 == 0) ? febDay = 28 : febDay = 29;
y = changeCalYear; //年へ代入
month = changeCalMonth + 1; //月へ代入
day = changeCalDays; //日へ代入
calendar.value = y + "-" + zeroPad(month) + "-" + zeroPad(day);
});
};
////////////////////
// ▼▼▼ コンポーネント
////////////////////
// ▼ 停止、再開ボタン
class StopBtn extends React.Component {
constructor(props) {
super(props);
this.yearDate = this.yearDate.bind(this);
this.clickStopBtn = this.clickStopBtn.bind(this);
}
// ▼ カレンダー表示のロジック
yearDate(monthDays, febDay) {
if (!pause) {
day = day + 1 * r;
// うるう年のロジック
!(y % 4 == 0 && y % 100 != 0 || y % 400 == 0) ? febDay = 28 : febDay = 29;
// 月による日数
if (month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) { //31日の月の場合
monthDays = 31;
} else if (month == 4 || month == 6 || month == 9 || month == 11) { //30日の月の場合
monthDays = 30;
} else { //その他 2月(うるう年のロジック)
monthDays = febDay;
}
if (r > 0) { //通常の経過
if (day > monthDays) {
day = 1;
month++;
}
if (month > 12) {
month = 1;
y++;
}
} else if (r < 0) { //逆回転
if (month == 5 || month == 7 || month == 10 || month == 12) { //31日の月の場合
monthDays = 30; // 表示されている前の月の日数
} else if (month == 1 || month == 2 || month == 4 || month == 6 || month == 8 || month == 9 || month == 11) { //30日の月の場合
monthDays = 31; // 表示されている前の月の日数
} else if (month == 3) { //その他 2月(うるう年のロジック)
monthDays = febDay;
}
if (day < 1) { //1日未満になったら
day = monthDays;
month--;
}
if (month < 1) {
month = 12;
y--;
}
}
this.countTimer = setTimeout(this.yearDate, timer); //clearTimeout のため変数に代入
calendar.value = y + "-" + zeroPad(month) + "-" + zeroPad(day); //日付の変更
}
}
// ▼ ボタンクリックの関数
clickStopBtn() {
let stopBtn = document.getElementById('stopBtn');
pause == false ?
(pause = true, stopBtn.textContent = '再開', clearTimeout(this.countTimer)) // タイマー一時停止
:
(pause = false, day = day - 1 * r, stopBtn.textContent = '停止', clearTimeout(this.countTimer), calenderChange()); // タイマー再開
};
render() {
return <p id="stopBtn" className="btn" style={btn} onClick={() => {
this.clickStopBtn(); this.yearDate();
}}> 開始</p >;
}
}
// ▼ 速度ボタン
class SpeedBtn extends React.Component {
constructor(props) {
super(props);
}
// ▼ ボタンクリックの関数
clickSpeedBtn(btn) {
btn = document.getElementById('btn');
switch (timer) {
case 1000:
// ▼ シフトボタンが押されていたら
event.shiftKey ? (btn.textContent = 'x1000', timer = 1) : (btn.textContent = 'x10', timer = 100);
break;
case 100:
// ▼ シフトボタンが押されていたら
event.shiftKey ? (btn.textContent = 'x1', timer = 1000) : (btn.textContent = 'x100', timer = 10);
break;
case 10:
// ▼ シフトボタンが押されていたら
event.shiftKey ? (btn.textContent = 'x10', timer = 100) : (btn.textContent = 'x1000', timer = 1);
break;
case 1:
// ▼ シフトボタンが押されていたら
event.shiftKey ? (btn.textContent = 'x100', timer = 10) : (btn.textContent = 'x1', timer = 1000);
break;
}
}
render() {
return <p id="btn" className="btn" style={btn} onClick={this.clickSpeedBtn}>x1</p>;
}
}
// ▼ 逆回転ボタン
class ReverseBtn extends React.Component {
constructor(props) {
super(props);
}
clickReverseBtn(reverseBtn) {
reverseBtn = document.getElementById('reverseBtn');
r *= -1;
r > 0 ? (true, reverseBtn.textContent = '逆回転') : (false, reverseBtn.textContent = '正回転')
}
render() {
return <p id="reverseBtn" className="btn" style={btn} onClick={this.clickReverseBtn}>逆回転</p>;
}
}
////////////////////
// ▼▼▼ JSX
////////////////////
let element = (
<div id="btnWrap" style={btnWrap}>
<SpeedBtn />
<StopBtn />
<ReverseBtn />
</div>
);
ReactDOM.render(element, document.getElementById('root'));
</script>
</body>
</html>
仕様
最初は停止しています。
開始クリックで1秒ごとに日付が増えていきます。
再生中の表示が停止に変わり、停止するとカレンダー進行が停止、表示が再開になります。
x1をクリックすると10倍づつ速度が増し、表示もx10、x100、x1000と変化します。
シフトボタンを押しながらだと1/10づつ減速します。
逆回転クリックで進行が逆になり、表示も逆回転になります。
手動でカレンダーの日付を変更すると進行がストップし、再開すると変更した日付から再開します。
苦心した末、なんとか動いたけど、ずいぶんと中途半端な書き方をしてしまいました。
ボタンだけコンポーネントに分けましたが、viewと機能を同梱してしまい、最小化の概念に従ってないような気がします。
冒頭で紹介した『React.js & Next.js超入門』のレビューにありましたが、最近のReactはclassコンポーネントより関数コンポーネントが主流とのことです。
今回はプロジェクトにせずに1枚のhtmlに書きました。
以降、node.jsのインストールやコンポーネントの分割、プロジェクトの書き出しなど扱っていきたいと思います。
まだまだ始めたばかり。
今後も習得に励みたいと思います。
最後まで読んでくださりありがとうございました。
参考にしたサイト
公式Reactサイト
maesblog さま