はじめに
私がReactに出会い1年程度経過し、React自体にも大きな変化がありました。今回は、Reactの良さを知ってもらいたいと思い、筆を取りました。
また、改めて自分の中のReactの知識を整理しようというのがサブ目標です。何回かに分けて1つのアプリを作れる程度までを記事にまとめていきたいと思ってます。
筆者のステータスは以下の通りです
- React・Next.jsを使って簡単なアプリを作ったことがある
- アルバイトでSpringを使ったwebアプリの開発をしている**(フロントはjQueryゴリゴリ)**
Reactに出会って使用するに至った経緯
概略: jQueryで地獄を見たためReactに救いを求めました。
前述の通りjQueryを良く使用していて基本は問題なかったのですが、画面項目が増え動きがリッチになるにつれ実装難易度やメンテナンス性に限界を感じてきました。
そこでたまたま出会ったのがReactでした。しかし、調べてみても*「MVCのVのみを担当するフレームワークで ──」「仮想DOMが──」*などと説明されていても「は?」って感じで、イマイチどういったモノなのか掴めずにいました。
しかし実際に使ってみると、実にシンプルなフレームワーク/ライブラリであり、悩みを解決してくれる存在であることがわかりました。元々JSが好きだったのもありとても馴染みやすかったです。
Reactとは
公式ドキュメントには以下のように紹介されてます。
A JavaScript library for building user interfaces
ユーザインターフェース構築のための JavaScript ライブラリ
これだけではわからないので、主な機能の説明と実装例を用いて紹介していきたいを思います。
Reactを使うと何がいいのか?
こちらも公式ドキュメントのトップから抜粋
- Declarative (宣言的なView)
- Component-Based (コンポーネントベース)
もう1つ「Learn Once, Write Anywhere (一度学習すれば、どこでも使える)」ということが挙げられていますが、これは一旦スルーで。
Declarative (宣言的なView)
まず「宣言的」って何?というとこですね。
Reactで実装する際に私たちが実装するのは**「ある状態において何が表示されるべきか」です。
「この値をここに表示して」、*「この場合はこっちを表示して」*というのをただただ宣言**していけば良いわけです。
そしてその宣言通りに効率良くUIの更新(DOMの操作)をする必要があります。
そこで登場するのが仮想DOMです。仮想DOMは通常のDOMと同じツリー構造をとります。
仮想DOMが構築し直されると、差分のみが実際のDOMに反映される、といった具合です。
仮想DOMの構築はサーバーサイドレンダリング(SSR)でテンプレートエンジンを使ってHTMLを生成するのに良く似ています。SSRでの描画で画面の値が不整合になったなんていう記憶はないので、それに近い方法を取れる仮想DOMを使わない手はありません。
加えて、必要な箇所のDOMの更新しか行われないため、下手なピュアのJSよりもパフォーマンスが向上します。
Component-Based (コンポーネントベース)
ReactではUIの状態、見た目、動きをコンポーネントという単位で分割します。単純な入力欄からページまで全てをコンポーネントとみなします。コンポーネントを組みわせていくことでUIを構築していきます。
分割することで再利用が可能になり、全体的な見た目の統一感を生みます。
コンポーネントは自身の状態を管理し、見た目と振る舞いを定義します。そのため状態をDOMに置くことがなくなります。DOMにある値はどこからでも変更可能ですが、コンポーネントの状態はアクセスできるスコープが限られるので状態の遷移が追いやすくなります。
Reactを書いてみる
九九を表示するアプリを考える
- 何の段を表示させるかを入力する
- 入力された段の九九の表を表示する
jQueryの場合
Reactで書く前に比較対象としてjQueryを使った場合の実装をのせておきます
See the Pen kuku_jQuery by nabekou (@nabekou29) on CodePen.
data属性にかける値を持たせておいて、値が入力された際に、data属性のあるtdタグのテキストを書き換えていく感じです。
Reactの場合
こちらがReactでの実装です。説明しやすさも考えてTypeScriptで書いてます。
See the Pen kuku_React by nabekou (@nabekou29) on CodePen.
解説
const App: React.FunctionComponent<{defaultValue: number}> = ({defaultValue}) => {...}
この関数がコンポーネントです。(Classで定義する方法もあります。)
属性を引数に受け取ることができます。
const [num1, setNum1] = React.useState(defaultValue);
React.useState
を使うことでこのコンポーネントで状態を持つことができます。
num1
が状態、setNum1
がnum1
を更新するための関数です。setNum1
を通して値を更新することでReactに値が変わったことを知らせています。
return (
<div>
<input type="number" value={num1} onChange={handleNum1Change} />の段
<table>
<tbody>
<tr><th>1</th><td>{1 * num1}</td></tr>
{/* 省略... */}
</tbody>
</table>
</div>
);
関数のコンポーネントでは、React要素をreturn
します。React要素はJSXにより作成可能です。コンパイルすることでReact要素を作成する関数に変換されます。(変換後はCodePenの「View Compiled」ボタンから確認できます)。
このHTMLっぽい書き方がJSXです。あくまでそれっぽいだけなので、本来存在しない属性や一部名前が変わっている属性が存在します。JSXのなかで{}
で囲まれた中にJSを書くことが可能です。当然そのJS内でもJSXを書けます。
const handleNum1Change = (event: React.ChangeEvent<HTMLInputElement>) => {
setNum1(Number(event.target.value));
}
値を入力した時にnum1
を更新します。setNum1
を使って更新することで表の値が更新されます。
ReactDOM.render(
<App defaultValue={1}/>,
document.getElementById('app')
);
React要素を#app
に描画しています。
ちなみに
<table>
<tbody>
<tr><th>1</th><td>{1 * num1}</td></tr>
<tr><th>2</th><td>{2 * num1}</td></tr>
<tr><th>3</th><td>{3 * num1}</td></tr>
...
<tr><th>9</th><td>{9 * num1}</td></tr>
</tbody>
</table>
HTMLに寄せて書きましたが、Arrayを用いて以下のように書くことが出来ます。
<table>
<tbody>
{
[1, 2, 3, 4, 5, 6, 7, 8, 9].map(num2 => (
<tr key={num2}><th>{num2}</th><td>{num1 * num2}</td></tr>
))
}
</tbody>
</table>
比べてみる
- Declarative (宣言的なView)
- Component-Based (コンポーネントベース)
上の2つを意識して比べてみようと思います
宣言的なView 的には?
特に<td>{num1 * num2}</td>
のところがとてもわかりやすくなっていると思います。表に何が表示されるかがより明らかになりました。
また、更新がとても楽になっています。jQueryでは各td
要素にアクセスして計算・更新をしていましたが、Reactではnum1
を更新するだけで表の値が更新されています。
コンポーネントベース 的には?
見た目と動きが一体となったことで、状態の変化がどのように行われて、どういった作用をもたらすのかがぱっと見で分かるようになりました。JSとHTMLを行ったりきたりする必要が無くなりました。
さらに、変にセレクタを書いたりする必要性が無くなりました。どうせ密な結合になるならこっちの方がわかりやすいと思います。
おわりに
Reactは学習コストが重いと良く言われている気がしますが、覚えることは圧倒的に少ないです。ただJSに対する知識がそれなりに求められるので、そこが難しいところなのかなと思います。Hooksの登場で書きやすさもかなり向上したので、これからも周りに布教していきたいと思います。
次はもう少し実装よりの話をする予定です。
参考
React公式
Reactを使うとなぜjQueryが要らなくなるのか
ReactとVueのどちらを選ぶか
フロントエンドのコンポーネント設計に立ち向かう