こんにちはカレイドです。
みなさんはReactというJavaScriptフレームワークをご存知でしょうか。
ReactとはJavaScriptのフレームワークで最近流行りが加速しつつあります。
JavaScriptは知っているけれどReactは知らないっていう人もいるんじゃないでしょうか。
今回はReactではどんなことが起こっているかを簡単に理解してもらいつつ、よくみるProgressBarを題材にコードを紹介していこうと思います。
Reactのprogress barです。
見た目なんていくらでも後でかっこよくできるのでflexbox使ってどうやるか考えてみてください👌僕はCSSのプロじゃないですが練習すればこの位は弄りながら出来るようになります。
動的に塗れる範囲が変わるのとテキスト変化がポイントですね🤔 pic.twitter.com/gEQQGCyOYp
— カレイド@独学のみ4ヶ月でベンチャーエンジニアᵃⁿᵈ半年でブログ収益1万達成 (@kaleido0101) September 11, 2019
Reactはこれを応用してWebサービス、Webアプリを作ることもできる、可能性に満ち溢れたものです。
ただし、初学者がよくHTML/CSSを学んだ後によくReactに手を出してみたけど難しくてわからないという声をよく聞くので今日は実際にみなさんにコードをみてもらいながら簡単な解説をしていこうと思います。
この講座ではある程度変数などの知識を知っていることが望ましいので不安な方は
【プログラミング】HTML/CSSを学んだら次はJavaScript!最低限の知識を紹介します
JavaScriptからReactへ。map関数,三項演算子、論理演算子【プログラミング】
JavaScriptからReactへ【 async/await、 filter関数、分割代入、スプレッド演算子】
この辺の記事を読んでもらえるといいんじゃないかなと思います。この記事でも簡単にReactに触れているのでぜひ参考にしてみてください!
また、今回は生JSやJQueryを触ってきた人たちに
っていうのがわかってもらえたら嬉しいです。
そして一応CSSで知っておくべきFlexBoxについても書いていますのでHTML/CSSちょっと勉強したよって人もチャレンジしてみるといいんじゃないかなと思います。
カレイドの簡単な自己紹介です。
こんな方におすすめ
- HTML/CSSを学びFlexBoxを学んでいる方、同様にパーツを配置するか実践ベースで学びたいかた。
- Reactを聞いたことあるがざっくりでいいから感覚を掴みたい人
- 新しい技術に積極的にみてみたい方
- よくみるProgressBarがどのように動いているか興味がある人
What is React ?
さて、簡単にですがReactの説明をしておこうと思います。
Reactがない時代、Webアプリを読み込んで別のページへ行こうとする場合を考えましょう。
イメージ通り今見ている紙芝居から別の紙芝居へと移動をすることになります。
つまり複数のHTMLファイルを行き来します。
しかし、一枚の紙芝居のなかで少しだけ変化があったとしましょう。それを表現するためにはご存知の通り少しだけ変えた別の紙芝居を用意する必要があります。こういうのを差分というのですが、この小さい差分を得るためにHTMLファイルを丸ごと差し替えるのは少し効率が悪いですよね。
そこでSPAと呼ばれる技術があります。
SPAとは(Single Page Application)と言われ一枚の紙芝居のアプリケーションをイメージしていただければOKです。
その通りです。なのでHTMLの力だけではこれを実現することができません。そこでJavaScriptの出番です。
JavaScriptはよくHTML/CSSに動きをつけるものと言われていますが事実、HTMLの上に置かれた各々のパーツを自由に操る能力を持っています。
しかし、残念ながら生のJSを勉強したことがある人はわかるかもしれませんが、めちゃくちゃ大変です。これならHTMLでページを複数作った方がいいと思えるほど楽なのです。
それを解決したJavaScriptフレームワークこそがReactなのです。
Reactには何を書いていくかというと、
- 表示したいHTML
- それをどう動かしたかのロジック
を書きます。CSSをどこに書くかはまだまだ意見や、プロジェクトによりけりですが、CSSファイルを直接書く。もしくはJSの中にそのまま書くこともできます。
というわけで、中身に入る前に一瞬だけ基本的なJSを見ておきましょう。わからない人も呪文だと思えばOKです。
こちらのコードを参考に進めていきます。
簡単なイメージとしては一つのファイルでlogicとviewの両方をデザインしていきます。
<div id="root"></div>
まずはHTMLファイルの記述ですが、おなじみのdivタグにidのrootが付与されています。
このdivタグをJS側から操作します。
JSの中身
まずは先に一番下へいくと以下の記述があると思います。
ReactDOM.render(<App />, document.getElementById("root"));
ReactDOM
と呼ばれるものがHTML側に記述しておいたdivタグを指していて <App />と呼ばれるコンポーネント(パーツ)をレンダリング(表示)することを意味します。
おそらくすでにわからねーよって方もいると思うので図で見てみましょう。
その通りですね笑
表示する<App />は今からその中身を見ていきます。
つまりどのように自分が画面上に表示するかをこれから記述していきます。
<App />の中身をチェック
では<App />の中身を見ていきましょう。
Appの中身は何かと言うと実は関数なんですね。もっと詳しく言えばJSXと呼ばれるものをreturnする関数です。
JSXとは何かわからない人もいると思いますが、ここではJavaScriptの世界でHTMLのような記述をしたものという風に捉えておけばよいでしょう。JavaScriptなので当然動的な処理も組み込むことができます。
const App = () => {
const [text, setText] = React.useState("");
const [length, setLength] = React.useState(0);
const [now, setNow] = React.useState(0);
return (
<div>
<ProgressBar now={now} length={length} text={text || "Now Loading..."} />
<div>表示テキスト</div>
<input name="text" onChange={e => setText(e.target.value)} value={text} />
<div>全体の長さ</div>
<input
name="length"
type="number"
onChange={e => setLength(e.target.value)}
value={length}
/>
<div>現在の個数 {now}</div>
<input
name="text"
type="range"
max={length}
step={1}
onChange={e => setNow(e.target.value)}
value={now}
/>
</div>
<App />
をレンダリング(お絵かきだと思ってください)する際にこの関数の中身を上から下へ処理して最後のreturnで最後の結果をまとめて一つのパーツ(JSXを返します)
ここでの中身を図にするとこんな感じですね。
return内部のJSXにおいてJavaScript表記をしたい場合は { }でくくってあげればその内部でJavaScript表記ができます。
JavaScriptからReactへ。map関数,三項演算子、論理演算子【プログラミング】
JavaScriptからReactへ【 async/await、 filter関数、分割代入、スプレッド演算子】
でも解説しているので参考までに。
そして onChange={e => setNow(e.target.value)}
は新しい部分になると思います。
まずはReactを勉強する上で知っておいてもらいたい二つの物を紹介したいと思います。onChange
の中にあるsetNow()
の説明に必要になるものです。
propsとstateという二つの存在
まずは簡単なイメージですが、このように一番上の<App /> から下の <ProgressBar /> そしてさらに下の階層へ続けるようにどんどんピラミッドのように下へ階層を続けることができます。
その際に下の階層へこの情報は使うから伝達しておきたいなーなんてものがあります。つまりピラミッドの上層から下層への情報伝達。
そのデータを保持しておくオブジェクトがpropsになります。
propsの役割
propsは上の階層から下の階層への情報伝達を行ってくれます。
ここで大事なのは読み取り(Read)はできます。しかし、書き変え(write)はできません。
またpropsはどの階層までもデータを持っていくことが当然できます。なので10階層上の社長から新入社員まで情報を伝達することも出来ます。
そして勘違いとしてあるのがそのデータを一番下で書き換えたら本来は全ての階層でデータを書き換えることが本来の挙動ですが、それは書き換えの権限が今はないようにそのままでは変更は不可能です。
上司からデータをもらって例え間違いを発見したとしてもそのデータを直接書き換える権限は持っていないと思ってもらえればOKです。
では下位の階層から値を書き換えて、上位の値も全て書き換えてしまうことは出来ないのでしょうか。
もちろんできます。厳密には上位で作っておいた関数を下位の階層で発火させるのです。そのためにstateと呼ばれる存在が必要になります。
値を保持する特別な変数state そして強制的に再レンダリングと値を書き換える関数setState()
それではもう一つ重要な役割を果たす state
の存在について説明をします。
はい。その通りですね。
じゃあなんのために存在するかというと、単に値を書き換えるだけでは内部では書き換えたことを知っていてもViewにはその書き換えは反映されません。つまり、再レンダリングをかける必要があります。
そしてpropsでも説明したように下の階層でも変更権限をもたせたい場合、この書き換えと再レンダリングを発火させるsetState()を含む関数を下の階層へ送り込むことで下の階層から発火させて、変更自体は一番上の階層で起こっているような状況を作り出す必要があります。
なのでstateには書き換えが起こったら再レンダリングを起こしたいもの、もう少し先勉強するともっとグローバルにデータを持っておきたい物にはstateを利用してデータの格納を行っていきます。
今回は簡単なstateだけ見て次回のアプリで複雑なstateを見る予定です。
それでは<App />内部にいた
const [text, setText]=React.useState()
const [length, setLength]=React.useState()
const [now, setNow]=React.useState()
がstateの中身になります。順番に見ていきましょう。
React.useState()
React.useState()
を使うと返り値として二つの値が配列として帰ってきます。というのは前回の記事で説明しました。
1つわかれば他もわかりますので安心してくださいね。
const [text, setText]=React.useState("")
まず、useState()
関数で引数を取ることができます。その引数はいわゆる初期値と言われるものですね。今回はtextに""(空文字)が格納されることになります。
今回は返り値の1番目にtext
, 2番目にsetText
という名前をつけています。このtext
はProgressBarでお見せしたようにNowLoding...
に相当する部分です。
ユーザーの入力に対してその都度画面のレンダリングが変更されていることが、codepen上で確認できると思います。
ユーザの入力に対して変更を適用させるのがsetText()
と呼ばれる関数になります。
setText()
は関数であり、引数に変更内容を入れることで値を更新して再レンダリングを行います。
なかなか理解することが難しいと思いますのでもう一度おさらいです。
React.useState("")
は引数を取ることができてそれは初期値として扱われる(今回ならtext="" として ""が初期値)
React.useState("")
は配列を返し、一つ目には値を格納する変数、二つ目には値を更新するための関数を返す。
そして
という質問に対してはcodepen場で試してみるとわかるように結論からいうとダメ。
なぜだめか、それは今回のように例えばtext
に新しくtext="更新中"
としたときに残念ながらこれではReactは画面上に更新中とは表示してくれず、再レンダリングまで強制するのがsetText()
でした
まぁそもそもconstでtext
を定義しているので再代入はできないのですが...笑
残りは同様に全体の長さを制御するlength
,setLength
今の長さがいくつかを生業する now
,setNow
になります。
通常第二引数にはsetHogeのように前にsetをつけるのが慣習です。
これでようやくonChange()
の説明ができますね。
onChangeの役割
inputのプロパティにはonChangeというプロパティが存在します。onClickなどonがつくものは基本的には〜〜したときに発火をします。
onChange={e => setNow(e.target.value)}
onChangeはinputの中身を書き換えた時に発火するものです。今回は何がおきたかを含んでいるオブジェクトを e という引数に代入しています。
このeはあくまで受け取りの変数名で eventという名前をつけてもいいし myEventでもなんでもOKです。
それを e.target.value
として今の値をsetNow
に渡すことで値を更新して再レンダリングを行なっています。
このe.target.value
はよく使うので丸暗記で大丈夫ですね。
だいぶ説明が長くなりましたがこの辺りの理解が難しい部分だと思いますので実際に手を動かしてみて試すのが1番の近道だと思います。
そして
<ProgressBar now={now} length={length} text={text} />
の部分でもう一つのpropsが活躍をすることになります。
<ProgressBar /> の中身
それではいよいよ最後の大元であるProgressBarについてみていこうと思います。
<ProgressBar/>の内部でpropsが果たす役割
内部をみてみるとpropsという引数を受け取っていることがわかると思います。そして
const ProgressBar = props => {
const { now, length, text } = props;
props
の中に先ほど送り込んだnow
,length
,text
というプロパティが含まれていることがわかると思います。
先ほど説明したようにReactでは下位のコンポーネントへと情報を送り込むことができます。
props
はオブジェクトであり、この場合だとProgressBarの内部でnow
,length
,text
というプロパティでその内部でもともとAppの内部で使っていたnow
,length
,text
という変数を送り込みます。
同じ名前でわかりにくいと思いますので理解を深めるためのイメージとしては
<ProgressBar barNow={appNow} barLength={appLength} barText={appText} />
こんな感じです。barがついている方はProgressBarの中での変数名です。appがついている方はApp側で定義されている変数名ということになります。
この場合ならその内部で
const { barNow, barLength, barText }=props
というように取り出せます。
中身のロジックを読み解く
今回のテーマの一つでもあるようにProgressBarそのままを表示してくれる大元のコンポです。Reactでは自分が定義したコンポーネントには大文字から始める制約がついています。なので<App />もそうでしたね。
<input />は小文字から始まるのでもともとHTMLでもあるinputと全く同じです。逆に<Input />なんて書いてあればそれはどこかに別に定義されたコンポーネントということになりますね!importされている部分を辿ればそのコンポへといけるはずです。
中身は意外とシンプルで
const percent = Math.floor((now / length) * 100);
ここで全体の進行状況を計算して、percentに格納します。
各スタイルはclassName
というところにそのまま当てるだけです。HTMLで定義されるclassと区別するために別の名前がついていると理解していただければOKです。
最後の関門は
{percent === 100 ? (<p className="text">Done</p>) : (<p className="text">{text || "Now Loading..."}</p>)}
ここでしょう。こういうのはじっくり分解して理解するに限ります。まずは全体を区切ってみると三項演算子がキーになっているとわかります。
つまり
{percent === 100 ? A : B}
のような構造であるとわかります。内容を解釈すればpercentが100であればAをそうでなければBを実行することになります。
なのでpercentが100なら
<p className="text">Done</p>
そうでなければ
<p className="text">{text || 'Now Loading...'}</p>
がきます。さらにその内部において
{text || 'Now Loading...'}
が来ていますがこれはpropsで渡ってきたtextがfalseと判定されるものでなければtextの値を使ってfalseであればNow Loadingが表示されるという解釈ができるんでしたね。
この辺の内容は過去記事で紹介しているので要チェックです。
JavaScriptからReactへ。map関数,三項演算子、論理演算子【プログラミング】
最後の
<div className="right-side">
<p className="text">{`${now} / ${length}`}</p>
</div>
の部分では特殊な書き方をしていますが、バッククォーツで挟み込むことで複数の変数を入れることができます。変数は$ { }の中にかけばOKです。
終わりに 次回は複雑なstateを見てみよう
いかがでしたでしょうか。実際に見ているprogressBarの中身を見てみて、難しそうだなと思うか意外と自分でも理解できそうと思うか結構分かれると思いますが、大事なのはどんな機能も崩して考えると小さなロジックから生まれているというところです。
カレイドは今、ベンチャー企業でスマホアプリを作っていますが出来上がっているアプリはいろんな機能揃っててすごいなーと思いますが、
一個一個丁寧にその機能を見ると今回説明したようなpropsとstateなどをしっかり使いこなして一つの機能が出来上がっています。
なので、どんな言語を学ぶにしてもその言語で使う必須スキルの用途を使いこなせることが大事なんだなーと改めて思います。
これを機に興味を持った人はぜひプログラミング学習の一歩目を始めてみませんか?