Reactでカウントタイマー機能を作る①
【目標】
Reactでカウントタイマー機能を作る
【困ったこと】
以下の通りコードを書いたのだが、npm startでブラウザ表示しようとしたところ、エラーになってしまった。
import React, {Component} from 'react'; class Timer extends Component{ constructor(props){ super(props); this.state={ remainMSec:1500000, //初期の残り時間は25分 remainM:this.remainMSec.getMinutes(), //ミリ秒を「分」の形で取得する remainS:this.remainMSec.getSeconds(), //ミリ秒を「秒」の形で取得する label:`${this.remainM}:${this.remainS}` }; this.oldTime=Date.now(); //oldTimeでスクリプト開始時の時間を記録する this.doCountdown = this.doCountdown.bind(this);A } timerStyle = { fontSize: '40px', color: 'hsl(213,100%,69%)' } btnStyle = { fontSize: '20px', padding: '10px', backgroundColor:'hsl(213,100%,69%)', borderRadius: '10px' } doCountdown(){ const timerId = setInterval(()=>{ /* setInterval内でDate.now()を実行することで、1秒ごとに更新される現在の時間をcurrentTImeという名称で取得することができる。*/ const currentTime=Date.now(); //差分を求める const diff=currentTime-this.state.oldTime; //残りのミリ秒を計算し、remainSecを更新 this.setState((state)=>({ remainMSec:state.remainMSec-diff })); if(this.state.remainMSec <=0){ //カウントダウンを終了する。 clearInterval(timerId); this.setState((state)=>({ label:'Time Up!!' })); } },1000); } render(){ return <div> <p style={this.timerStyle} onClick={this.doCountdown()}>{this.state.label}</p> <p style={this.btnStyle}>スタート</p> </div> }; } export default Timer;
【問題の切り出し】
画像の通り「TypeError: Cannot read property 'getMinutes' of undefined」というエラー表示が出ていた。
おそらく「remainMSec:1500000」の部分で、stateの初期値としてDateオブジェクトではなく、数値を設定していたことに問題が
あるのではないか?
↓
remainMSecの定義を考え直すことから修正しようと思う。
『React.js&Next.js 超入門』Chapter4-1 Reduxを使ってみよう 覚書
『React.js&Next.js 超入門』P.190~209
React.js & Next.js超入門 | 掌田津耶乃 | 工学 | Kindleストア | Amazon
Reduxは思ってたよりも複雑だったので、頭の中を整理するために記事を書きました。
Reduxの重要な要素 ※自分なりの解釈です。
ストア(store)
データの保管庫。Reduxの中で最も重要な要素なのに、スクリプトの最後の方にならないと出てこない。
ステート(state)
ストアに保管される値のこと。初期値を設定した後はレデューサーの働きによっていろいろ変えられる。スクリプトでは一番最初に定義される。
レデューサー(reducer)
ストアが周りから影響を受けた時に、ケースに応じてステートの内容を書き換えるもの。
スクリプトの書き方
①reduxフォルダのsrcフォルダから「createStore.js」ファイルと「combineReducers」ファイルをインポートする。
import React from 'react'; import ReactDOM from 'react-dom'; import { createStore, combineReducers } from 'redux';
②react-reduxフォルダのsrcフォルダにあるcomponentsフォルダから「Provider.js」ファイルをインポートする。
import {Provider} from 'react-redux'; import './index.css'; import App from './App';
③ストアに保管する値(ステート)を定義する。
let state_value = { counter:0, message:"COUNTER" }
ここで設定した「state_value」という変数は②で定義するリデューサーの引数として使われる。
④ストアが周りから影響を受けた際にステートの値を書き換えるリデューサーを定義する。
function counter(state = state_value, actuion){ switch(action.type){ case 'INCREMENT'; return { counter: state.counter + 1, message: "INCREMENT" }; case 'DECREMENT'; return { counter: state.counter - 1, message: 'DECREMENT" }; }
ここで設定した「counter」というファンクションは③で定義するストアの引数として使われる。
⑤ストアを作成する。
let store = createStore(counter);
ここで設定した「store」は④のプロバイダーの属性として指定される。
⑥JSXタグの中でProviderタグを記述する。
ReactDOM.render( <Provider store = {store}> <App /> </Provider>, document.getElementById('root) );
③から⑥まで、それぞれの段階で作られた変数をわらしべ長者みたいに次のステップの引数やら属性として渡しています。
storeの作成が結構最後の方にあるのも、store自身が、Reduxの全ての要素を格納するコンポーネントということが関係しているのかもしれません。
■
作りかけのアプリをさくらサーバーに置いているのですが、久しぶりにhtmlファイルを開いてみたら、Font Awesomeから取得したアイコンが表示されなくなっていました💦
デベロッパーツールを確認してみました。
Access to fetch at 'https://ka-f.fontawesome.com/releases/v5.15.1/js/free-v4-shims.min.js' from origin 'http://browncobra29.sakura.ne.jp' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
↑
どうやら、この文章にエラーの原因が書かれているみたいです。
「オリジン 'http://browncobra29.sakura.ne.jp' から 'https://ka-f.fontawesome.com/releases/v5.15.1/js/free-v4-shims.min.js' へのフェッチへのアクセスは CORS ポリシーによってブロックされました。要求されたリソースには 'Access-Control-Allow-Origin' ヘッダーが存在しません。不透明な応答が必要な場合は、リクエストのモードを「no-cors」に設定して、CORS を無効にしてリソースをフェッチしてください。」
↑
和訳してみました。
CORSポリシーというのが、ポイントみたいです。
わからない単語
フェッチ・CORS
フェッチ 【 fetch 】
フェッチとは、取りに行く、取ってくる、持ってくる、連れてくる、来させる、呼び出す、引き出す、惹きつける、などの意味を持つ英単語。ITの分野では機器やプログラムなどが特定の場所からデータなどを読み出す動作のことを指すことが多い。
フェッチとは - IT用語辞典 e-Words
CORS
どうやらアプリを置いているサーバーとは別のサーバーからデータを持ってくる仕組みをCORS(Cross-Origin-Resorce-Sharing オリジン間リソース共有)というみたいです。自分はほとんど知識がないので、前半は内容を理解できたけど後半はさっぱりわかりませんでした。
qiita.com
【どうやったら問題を解決できるか?】
案① アプリを格納しているサーバーに、FontAwesomeのフォントのデータを保存する
案② CORSを無効にする
①の方が簡単そうに思えるけど、できるだろうか・・・?
React.js & Next.js超入門 リスト3-11
ただいま『React.js & Next.js超入門』を勉強しているのですが、サンプルコードの理解に苦労したので、忘れないよう自分なりの解釈を記録しておきます。
import React, {Component} from 'react'; import './App.css'; class App extends Component { constructor(props){ super(); this.title = props.title; this.message = props.message; } render(){ return <div> <h1>{this.props.title}</h1> <p>{this.props.message}</p> </div>; } } export default App;
正しくコードが書けていたら以下の画面が表示されます。
疑問
このコードを見て、以下の疑問を感じました。
①this.titleとthis.messageは一体何を意味しているのか?
②なぜ
this.title = props.title
と、this.titleという変数に引数propsの値を代入しているのか?
調査
①について
「this this javascript」というキーワードでGoogle検索してみました。
非常に参考になるQiitaの記事が見つかりました!
qiita.com
qiita.com
②について
「参考書に詰まったら別の参考書からヒントを探す」という方法を試しました。
具体的にはkindle unlimitedでReactに関する電子書籍を複数ダウンロードし、解説されているページを探しました。
自分なりの解釈
①について
上記のQiitaの記事JavaScript の this を理解する多分一番分かりやすい説明 - Qiitaの文章
this は function を呼んだ時の . の前についているオブジェクトを指している
と、JavaScriptの「this」は「4種類」?? - Qiitaの文章
重要なのは「呼び出し元」をみることです。
なぜなら「呼び出し元」に「this」は左右されるからです。
から、以下のように解釈しました。
サンプルコードのthisはclass Appを指し、またclass Appをnewした場合、生成されるインスタンス自身を指している。
thisは、thisが書かれた(呼び出された)大元のオブジェクトを参照する変数だと理解しました。
だから、class Appの中に書かれているthis.titleはclass App の title、this.messageはclass Appのmessageを指すのだと思われます。
②について
参考にしたのはナカノヒトシさん著の『はじめてのREACT JavaScript初心者でもわかるかんたんフロントエンド入門』です。
www.amazon.co.jp
核心をついた説明文があったので引用します。
propsはJSXの文法ではHTMLの属性値と同様の構文で与えることができ、コンポーネントからはthis.propsからアクセスすることが可能です。
この表示から以下のように考えました。
クラスコンポーネントでpropsの属性にアクセスするためには、thisを使わなければならない。thisを付けない状態ではpropsの属性は反映されない。
したがって、関数コンポーネントのpropsと違い、クラスコンポーネントでは、this.title = props.titleとthis.message = props.messageをコメントアウトして書いた場合、propsの引数にアクセスできないということになってしまいます。
ダメな例↓ このままでは何も表示されません!
class App extends Component { constructor(props){ super(); // this.title = props.title; // this.message = props.message; } render(){ return <div> <h1>{this.title}</h1> <p>{this.message}</p> </div>; } }
<App title = "App" message = "This is App Component!" />
このことから、きちんと
したがって、constructorの中でthis.title = props.title; this.message = props.messageと定義しているのだと結論づけました。
ちなみにthis.title = props.titleと言った表現を使わない場合、renderメソッドの呼び出す変数を this.props.title、this.props.messageと書いてあげても問題なく表示されます。
class App extends Component { constructor(props){ super(); // this.title = props.title; // this.message = props.message; } render(){ return <div> <h1>{this.props.title}</h1> <p>{this.props.message}</p> </div>; } }
間違ってたら恥ずかしいのですが、this.title = props.titleを使う場合は「class Appのtitleにはpropsで指定されているtitleが入るよ。」という意味合いに、this.props.titleを使う場合は「class App からpropsにアクセスして、titleの値を拾ってくるよ」という意味合いになるということで、結果同じ働きをするのだと解釈しました。
あくまで、個人的な解釈になります。誤りがありましたらご指摘いただけると幸いです。。。
30代からの(壁だらけの)プログラミング奮闘記
はじめまして。hiroです。 34歳の地方公務員です。 思い切ってブログはじめました。 このブログでは以下の目的にそって記事を投稿します。
◆ プログラミングに関する個人的な備忘録を作る
◆ 転職活動の際のポートフォリオ として使用できる資料を作る
◆ 自分と同じようにブログラミングでつまづきそうになっている人達に役立つような情報を提供する
途中でこの目的は変わるかもしれませんが、しばらくはこの主旨にそって記事を書いていきます。 下手な文章でも更新することを重視して運用したいと思います。 どうぞよろしくお願いします。