投稿日: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倍づつ速度が増し、表示もx10x100x1000と変化します。
シフトボタンを押しながらだと1/10づつ減速します。
逆回転クリックで進行が逆になり、表示も逆回転になります。
手動でカレンダーの日付を変更すると進行がストップし、再開すると変更した日付から再開します。
 

まとめ

苦心した末、なんとか動いたけど、ずいぶんと中途半端な書き方をしてしまいました。
ボタンだけコンポーネントに分けましたが、viewと機能を同梱してしまい、最小化の概念に従ってないような気がします。
冒頭で紹介した『React.js & Next.js超入門』のレビューにありましたが、最近のReactはclassコンポーネントより関数コンポーネントが主流とのことです。
 
今回はプロジェクトにせずに1枚のhtmlに書きました。
以降、node.jsのインストールやコンポーネントの分割、プロジェクトの書き出しなど扱っていきたいと思います。
まだまだ始めたばかり。
今後も習得に励みたいと思います。
 

最後まで読んでくださりありがとうございました。
 

参考にしたサイト
公式Reactサイト 
maesblog さま 

関連記事


 

Pocket