はじめに
空色では、自社サービスの管理画面にて、React + Redux + Typescriptをフロントエンドで使用しています。
というか、ちょうどこの技術を使った画面に置き換えるための移行期にあたるので、フロントエンドの知識を共有して、社内でも手を動かせる人を増やそうとしています。
もちろん、そんなのちょちょいのチョイやでの方、ぜひ一緒にいいもの作りましょう〜! -> Click
この記事の定義づけ
まず私は、「コードが動けばそれでよし」な思考ができません。
新しい言語やライブラリに出くわしたときに、それらの考え方と、フレームや規則に則って、「これがこうだから、こういうコードの書き方なのね!」がスッキリしないと理解が進まないのです。
なので、この記事は、私が業務でReact + Redux + Typescriptのコードを書き始める前に(というか実際には書きながら)、以下の点に意識して、ザックリまとめたものです。
- Reactってどうおいしいの?
- その中で出てきた気づきと具体例
Reduxに関してはアーキテクチャなので、その考え方ありきのReact,Typescriptだと思っているので、詳細は割愛します。この方の記事とかでなんとなく理解!
ちなみに、これらを学ぶ前の私の知識レベルは、Javascriptの基本はわかる!コードも簡単なのなら自力でかける!業務経験はほとんどなし!程度でした。
開発/実行環境
React + Redux + Typescript 2.3.3 + Webpack 2.6.0
前提:JqueryからReactへ
JQueryに取って代わって、一気に主流になってきたReact。
まず取って代わる前のJQueryの知識もまばらだったのでまとめ。
Jqueryは、命令的なJSライブラリ
- GUI構築のための Viewライブラリ。
- DOMの一部を書き換えて状態を変更するためのもの。
- DOM操作の命令を記述する。
- ちょっとコードが煩雑になり、どこでなにをしているかを追うのが大変。保守性に欠ける。
Reactは、宣言的なJSライブラリ
-
Reactは、単に与えられたpropsを元にviewをつくるだけのもの。
ReactのView処理の流れはこう。- 仮想DOM でまずツリー構造を再現
- 更新前の仮想DOMとの差分を抽出
- 実際のDOMに対しては最小のDOM操作だけ行う
つまり、DOMを操作するのではなく、状態からDOMを生成するためにコードを書く。コンポーネントはそのためにある。
プログラムは必ずしも処理の順番でなく、宣言順にプログラムを書く。
操作を重ねるわけではないので、冪統性があるのでデータさえ同じであれば、動作の再現が可能。不具合時に役に立つ説。
ちなみに,Reduxって
- Flux -> Reactのためのアプリケーションアーキテクチャ。単一データフロー(データの流れが親から子への一方向であること)を用いたもの
- Reduxは、Fluxの概念をさらに拡張したアーキテクチャらしい
- Action -> Dispatcher -> Store -> View(React)
- React
- Dispatcher -> Store -> View -> ActionCreator -> Dispatcher...
- この図がわかりやすくて、可愛い。笑
- Reduxは、Fluxの概念をさらに拡張したアーキテクチャらしい
なるほどね、いざ、実装!
・・・しようとしたけど。わからない単語がたくさんで大変なことに。
弊社のアプリケーションでは、RailsのJQuery+CoffeeScriptからReact+Typescriptに移行したので、どこからどこまでがどれなのかてんでわからない。。では1つずつ。
Typescriptとは
Javascriptの、固い版。
JSは型を定義しなくても、実行できちゃう。(Rubyもそうだったような。。)
ただし、ブラウザで実行しないことには間違いがわからないようでは、ユーザにとっても開発者にとっても不都合。
そこで、生まれたのがTypescript。コンパイルを走らせて、Javascriptに変換する。
書くのはちょっと冗長になってしまうが、CとかJavaやってきた人にとっては自然な感じ。
基本構文
別記事に飛びます。
やっとReact!
に入りたいところだけど、ソースコードがどうなっているかを読み解くのに必死。
まずは、各メソッドや宣言の意味から。初めましての皆さん。
基本構文
interface
- コンポーネントの関数宣言で、1つめの引数を宣言する。
-
props
とstate
のtypeの宣言をするもの。クラスではない。
interface SampleProps {
state: SampleState
onSetName: () => void
onChangeName: (event: React.FormEvent) => void
onSelectAction: (action: string) => void
}
props
- 他のComponent間でデータの受け渡しを可能にする箱。
- 1つめの引数が要素の属性を全て持つオブジェクトになる。
- propertyとしてくっつける値をinterfaceで設定しておいて、これを元にfunction呼んだりする。
- stateも、含めてOK
- propは、read-only
state
- コンポーネント内部の変数。
- イベントに対する操作に必要な値を入れておくための、オブジェクトを作るための、初期値。
-
constructor
-> 初期化のための特殊なメソッド -
let
で、interface
で設定したstate
を元にオブジェクトを作る。
let SampleState = {
sampleId: 1,
text: "sample",
isNamingCharacter: true,
}
React.formEvent / React.ReactNode / React.createElement
など
- Reactが用意している数少ないAPIたち。要素をどうにかするもの。
-
React.createElement
の使用例sample.tsxrootElement = React.createElement ('div', {}, "Hello, World") ReactDOM.render (rootElement, document.getElemntById('app'))
これが、レンダリングされるとこうなる。
sample.html<div id="app"> <div>Hello,World</div> </div>
コンポーネント化ってどうやるの?
-
createClass()
-> コンポーネントを定義するコンポーネント化前.tsxrootElement = React.createElement('div', {}, return React.createElement('div', {}, React.createElement('h1', {}, 'Brand'), React.createElement('ul', {}, React.createElement('li', {}, React.createElement('a', { href: '/about' }, 'About') ), React.createElement('li', {}, React.createElement('a', { href: '/news' }, 'News') ), React.createElement('li', {}, React.createElement('a', { href: '/contact-us' }, 'Contact us') ) ) )
コンポーネント化後.tsxvar GlobalNavigation = React.createClass({ render: function() { // rootElement = React.createElement('div', {}, return React.createElement('div', {}, React.createElement('h1', {}, 'Brand'), React.createElement('ul', {}, React.createElement('li', {}, React.createElement('a', { href: '/about' }, 'About') ), React.createElement('li', {}, React.createElement('a', { href: '/news' }, 'News') ), React.createElement('li', {}, React.createElement('a', { href: '/contact-us' }, 'Contact us') ) ) ) }, }) var rootElement = React.createElement(GlobalNavigation, {}) ReactDOM.render(rootElement, document.getElementById('app'))
その他の気づきなど
実装の順番
- Stateを最初に考える。あるイベントが起こったら、どうstateを変えるか?を実装していく。
- EventとActionの関係
- 多くの場合は、ユーザの操作によってDomEventが発火し、それに対するActionを定義する。
- Eventだけの場合も、ActionがEventに紐づいていない場合もありうる。
- returnするDOM要素は、1つにまとめる必要がある。だいたい
div
で囲む。
コンポーネントについて
- コンポーネントに属性を持たせる必要があるのは、2つ。イベントが起こったときに、何かを起こしたい。
- 今の状態(state)
- 動作、挙動(イベントハンドラ)
- コンポーネントとは、React.Nodeのデータ。ComponentはHTMLを取り扱っているように見えるが、実際はデータを返しているだけ。 最後に、ReactDom.renderはそのデータを受け取ってやっと適用する。
- jqueryだと、
$("<div>Hello!</div>")
を即時にHTML要素に変換する。ここが、JqueryとReactの大きな違い。 - ちなみにコンポーネントの実装方法は、大きく2つある
- クラス : stateの値を保持することができる。
- 関数 : stateを持たない。単純に、stateを変えるためのロジックを書くのみ。
- jqueryだと、
コンポーネントのそもそも意味とは?
Webページ制作の際は、コンポーネント思考でデザインをしてから、コーディングをすべきということ。部品をたくさん作り、部品ごとに設計をし、使いまわせるものは使い回す。そして部品ごとにコーディングしていく。
この考え方からすれば、いきなりコーディングから入るべきではなくて、まずは各コンポーネントがわかる絵を描いて、デザイナとエンジニアが理解してから、役割分担をして、進めていくべきなのかもしれません。
Tips:いまさら聞けないフロントエンド用語集
別記事に飛びます。
参考
http://www.s-arcana.co.jp/blog/2016/12/12/3438
http://zarari.hatenablog.com/entry/2016/04/11/212641