React始めました
最近までVue.jsを勉強していましたが、先日知人に「React >>> 越えられない壁 >>> Vue.js」と論破されてしまったので、ついカッとなってReactを始めました。
結果……完全にReactの軍門に下ることになりました。まだどちらが優位かまではわかりませんが、少なくとも私にはReactの方が向いているようです。
というわけで、Reactについて軽くまとめておこうと思います。
参考資料
-
React公式リファレンス
猫も杓子も公式リファレンス。Hooksは新しい機能らしく、まだ情報が整ってない部分もあるようです。 -
りあクト!
知人から書籍版を借りて読ませてもらいましたが、超絶わかりやすい。ものすごくわかりやすい。神。
これ読んでるならこの記事いらないんじゃないかな。DL版も取り扱っているので買い時ですね!
今回取り扱うReactについて
ここではTypeScriptで開発するReactについて紹介していきたいと思います。
また、最近Reactに追加されたと噂の機能をメインに利用します。
具体的には以下の通りです。
- Function Component
- React Hooks
Class Componentは一切使わないのでご注意ください。
また、TypeScript環境での開発を対象としています。
前提
前提条件として、以下のツール群を利用できる環境がそろっているものとします。
- node.js
- npm
- yarn
プロジェクト作成
ではプロジェクトを作成していきましょう。今回はCreate React Appを利用していきます。
$ npx create-react-app {プロジェクト名} --typescript
--typescript
は言わずもがなTypeScriptで作成するということです。
作成できたら、とりあえずpackage.json
のscripts
を確認しましょう。
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
start
、build
、test
については説明不要かと思いますが、eject
は見慣れませんね。
このeject
ですが、現在react-scriptsで管理しているモジュール類の依存設定等をすべて出力し、react-scriptsの管理下から外すことができるそうです。もちろん不可逆操作ですので、誤って叩かないように注意しましょう。不要なら削除しておいたほうが安全ですね。
コンポーネントの確認
それでは自動生成されたファイルを確認していきましょう。まずはsrc/index.tsx
から。
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
どうやらこのindex.tsxがエントリーポイントのようです。やっていることはAppコンポーネントの読み込みとDOM描画、およびserverの起動みたいですね。
server周りの処理はserviceWorkerがやってくれているようですが、こちらについては私もいまいち理解していないので省きます。
では次にsrc/App.tsx
を見ていきましょう。
import React from 'react';
import logo from './logo.svg';
import './App.css';
const App: React.FC = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
見ただけでおおよそ何をしているかはわかるんじゃないでしょうか。生成したJSXを返しているだけですね。
さんざんキモイと言われるJSXですが、このようにまとまっていれば綺麗に見えるのではないでしょうか。正直な話、私は初めて見た瞬間に感動を覚えました。
さておき、ここで注目してほしいのは以下の部分です。
const App: React.FC = () => {
return (
{JSX}
);
}
まず、TypeScriptの話について。
TypeScriptは静的言語のため、型の指定が行えます。React.FC
とはReact Function Component
の略だそうです。その名の通り、関数形式でコンポーネントを作成しています。
(こちらを参考)
次にReactのコンポーネントについて。
React Function Componentでは戻り値としてJSXを返します。ですが、通常の関数や後述するHooks等も記述することが可能です。要は最後にJSXを返せばOK。
コンポーネントの編集
それでは自分でコンポーネントを編集していきましょう。
フロントエンドでサンプルといえばカウンターアプリでしょうか。というわけで、ボタンを押せば数字が1増えるだけのアプリを作ってみましょう。
まずはApp.tsxの中身を空にします。
import React from 'react';
import './App.css'
const App: React.FC = () => {
return (
<div className="App">
</div>
);
}
export default App;
ここに記述していきますが、今回はReact HooksのuseState
を使います。
細かい説明は後にして、出来上がったコードはこちらになります。
import React, { useState } from 'react';
import './App.css'
const App: React.FC = () => {
const [count, setCount] = useState(0);
return (
<div className="App">
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>INCREMENT</button>
</div>
);
}
export default App;
コードを書き直したらyarn start
で起動してみましょう。ボタンをポチポチして数字が増えればOKです!
ではコードの内容を見ていきましょう。
const [count, setCount] = useState(0);
useStateは参照用の値と値を更新する関数のセットを生成します。このとき引数を初期値としますが、同時に型推論を行ってくれます。
今回は[count, setCount]という名前で生成しましたが、これらは[state, setState]という名前で呼ばれることが多いです。
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>INCREMENT</button>
useStateで生成したstateとsetStateはコンポーネント内で自由に使えます。
JSXでは{}
で挟むことでJavaScriptを記述できるようなので、h1
にcountを、button
のクリックイベントにsetCountを利用しています。
stateはJSXを含むコンポーネント内で参照することができます。
setStateは引数の値をstateに代入します。言い換えると、stateはsetStateを経由しなくては更新することができません。
コンポーネント中にsetStateを発火するインターフェイスを設置しておくことで、ユーザーはstateの更新を行うことができるようになります。
これらをまとめると以下のような図になります。
@startuml
storage state
storage setState
storage view
state --> view: 参照
view -right-> setState: 発火
setState -left-> state: 更新
@enduml
※なぜか画像がアップロードできないのでpluntUMLで記述
データが一方向に流れる、ということを覚えておきましょう。
コンポーネントの作成
それでは、カウンターの表示とボタンを別のコンポーネントに切り出してみましょう。
出来上がったコンポーネントは以下の通りです。
// App.tsx
import React, { useState } from 'react';
import './App.css'
import Count from './components/Count'
import Increment from './components/Increment'
const App: React.FC = () => {
const [count, setCount] = useState(0);
return (
<div className="App">
<Count count={count} />
<Increment count={count} setCount={setCount} />
</div>
);
}
export default App;
// components/Count.tsx
import React from 'react'
const Count: React.FC<{count: number}> = ({count}) => {
return (
<h1>{count}</h1>
)
}
export default Count;
// components/Increment.tsx
import React, { Dispatch } from 'react';
const Increment: React.FC<{count: number, setCount: Dispatch<number>}> = ({count, setCount}) => {
const clickEventHndler = () => {
setCount(count + 1);
}
return (
<button onClick={clickEventHndler}>INCREMENT</button>
)
}
export default Increment;
いささか冗長でしょうか? 実際には独自のボタンやテキストエリアを作成するのでなければ、ここまで小さなコンポーネントを作ることはあまり無いかと思います。
各コンポーネントが何をしているかは見ただけでおおよそわかるかと思います。注目してほしいのは次の2点です。
<Count count={count} />
<Increment count={count} setCount={setCount} />
インポートしたコンポーネントを指定しています。Reactではコンポーネントはタグと同じように表記できます。
属性としてpropsを子コンポーネントに与えることができます。どうやらReactには「データの流れは常に 親→子 となる」という思想があるようです(冒頭の知人曰く。ソースはまだ未確認です)。
今回でも例に漏れず、データは子に対して一方的に渡されます。setCount
はcount
の更新を行える関数ですが、あくまでホストは親コンポーネントになります。
const Increment: React.FC<{count: number, setCount: Dispatch<number>}> = ({count, setCount}) => {
Javaやswiftの経験者ならご存知かもしれませんが、ここではジェネリクスという機能を使っています。詳細は各自で調べていただきたいのですが、簡単に言うと引数の型をこちらで指定することができます。
今回のケースでは{count: number, setCount: Dispatch<number>}
と指定しています。つまり、number型のcount、およびnumber型の引数をとるDispatch型のsetCount、この両者をもつオブジェクトを引数としています。他の型は引数にとることができません。
ジェネリクスは強力な機能ですので、ガンガン使って柔軟かつ安全にコーディングしていきましょう。
まとめ
今回はかなり初心者向けの記事になってしまいましたが、Reactは扱っていてとても楽しいですね。
今後も開発を続けて記事もちょこちょこ書いていきたいと思います。
ついでと言っては何ですが、エンジニアとして論破されないだけの理論もしっかり身につけていきたいところです。
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
0件のコメント