JavaScript
TypeScript
React

Reactってなにそれおいしいの?〜導入から基本の実装〜

More than 1 year has passed since last update.

はじめに

空色では、自社サービスの管理画面にて、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処理の流れはこう。

    1. 仮想DOM でまずツリー構造を再現
    2. 更新前の仮想DOMとの差分を抽出
    3. 実際のDOMに対しては最小のDOM操作だけ行う
  • つまり、DOMを操作するのではなく、状態からDOMを生成するためにコードを書く。コンポーネントはそのためにある。

  • プログラムは必ずしも処理の順番でなく、宣言順にプログラムを書く。

  • 操作を重ねるわけではないので、冪統性があるのでデータさえ同じであれば、動作の再現が可能。不具合時に役に立つ説。

ちなみに,Reduxって

  • Flux -> Reactのためのアプリケーションアーキテクチャ。単一データフロー(データの流れが親から子への一方向であること)を用いたもの
    • Reduxは、Fluxの概念をさらに拡張したアーキテクチャらしい
      • Action -> Dispatcher -> Store -> View(React)
    • React
      • Dispatcher -> Store -> View -> ActionCreator -> Dispatcher...
    • この図がわかりやすくて、可愛い。笑

なるほどね、いざ、実装!

・・・しようとしたけど。わからない単語がたくさんで大変なことに。
弊社のアプリケーションでは、RailsのJQuery+CoffeeScriptからReact+Typescriptに移行したので、どこからどこまでがどれなのかてんでわからない。。では1つずつ。

Typescriptとは

Javascriptの、固い版。

JSは型を定義しなくても、実行できちゃう。(Rubyもそうだったような。。)
ただし、ブラウザで実行しないことには間違いがわからないようでは、ユーザにとっても開発者にとっても不都合。
そこで、生まれたのがTypescript。コンパイルを走らせて、Javascriptに変換する。
書くのはちょっと冗長になってしまうが、CとかJavaやってきた人にとっては自然な感じ。

基本構文

別記事に飛びます。

やっとReact!

に入りたいところだけど、ソースコードがどうなっているかを読み解くのに必死。
まずは、各メソッドや宣言の意味から。初めましての皆さん。

基本構文

interface

  • コンポーネントの関数宣言で、1つめの引数を宣言する。
  • propsstateのtypeの宣言をするもの。クラスではない。
interface.tsx
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を元にオブジェクトを作る。
state.tsx
let SampleState = {
  sampleId: 1,
  text: "sample",
  isNamingCharacter: true,
}

React.formEvent / React.ReactNode / React.createElement  など

  • Reactが用意している数少ないAPIたち。要素をどうにかするもの。
  • React.createElementの使用例

    sample.tsx
    rootElement = React.createElement
    ('div', {}, "Hello, World")
    ReactDOM.render
    (rootElement, document.getElemntById('app'))
    

    これが、レンダリングされるとこうなる。

    sample.html
    <div id="app">
    <div>Hello,World</div>
    </div>
    

    コンポーネント化ってどうやるの?

  • createClass() -> コンポーネントを定義する

    コンポーネント化前.tsx
    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')
       )
     )
    )
    
    コンポーネント化後.tsx
    var 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つ。イベントが起こったときに、何かを起こしたい。
    1. 今の状態(state)
    2. 動作、挙動(イベントハンドラ)
  • コンポーネントとは、React.Nodeのデータ。ComponentはHTMLを取り扱っているように見えるが、実際はデータを返しているだけ。 最後に、ReactDom.renderはそのデータを受け取ってやっと適用する。
    • jqueryだと、$("<div>Hello!</div>")を即時にHTML要素に変換する。ここが、JqueryとReactの大きな違い。
    • ちなみにコンポーネントの実装方法は、大きく2つある
      1. クラス : stateの値を保持することができる。 
      2. 関数  : stateを持たない。単純に、stateを変えるためのロジックを書くのみ。

:point_up_tone1:コンポーネントのそもそも意味とは?

Webページ制作の際は、コンポーネント思考でデザインをしてから、コーディングをすべきということ。部品をたくさん作り、部品ごとに設計をし、使いまわせるものは使い回す。そして部品ごとにコーディングしていく。
この考え方からすれば、いきなりコーディングから入るべきではなくて、まずは各コンポーネントがわかる絵を描いて、デザイナとエンジニアが理解してから、役割分担をして、進めていくべきなのかもしれません。

Tips:いまさら聞けないフロントエンド用語集

別記事に飛びます。

参考

http://www.s-arcana.co.jp/blog/2016/12/12/3438
http://zarari.hatenablog.com/entry/2016/04/11/212641