eyecatch
Sun, Oct 7, 2018

React + Reduxアプリケーションプロジェクトのテンプレートを作る ― その8: Redux-Saga

ReactとReduxを学ぶために、開発環境というかプロジェクトテンプレートをスクラッチから作っている。 (最終的な成果はGitHubに置いた。) 前回はReact Reduxをセットアップした。 (adsbygoogle = window.adsbygoogle || []).push({}); ReduxのMiddleware Redux単体では同期的なデータフローしか実装できない。 つまり、Actionを発生させたら、即座にディスパッチされ、stateが更新される。 一方、非同期なフローとは、REST APIを呼んでその結果でstateを更新するような処理。 REST API呼び出しが非同期なわけだが、これをReduxのピュアなフローのどこで実行するのかというと、Middlewareで実行する。 MiddlewareはStoreのdispatch()をラップして、Actionをトラップして副作用を含む任意の処理をするための機能。 Middlewareの仕組みについてはこの記事が分かりやすい。 Middlewareには例えば、発生したActionの内容と、それによるstateの変化をログに出力するredux-loggerがある。 デバッグに有用そうなので入れておく。 yarn add redux-logger v3.0.6が入った。 Middlewareは、ReduxのapplyMiddleware()というAPIを使って、createStore()実行時に適用できる。 src/configureStore.js: -import { createStore } from 'redux'; +import { createStore, applyMiddleware } from 'redux'; +import { logger } from 'redux-logger'; import rootReducer from './reducers/rootReducer'; export default function configureStore(initialState = {}) { + const middlewares = []; + if (process.env.NODE_ENV === `development`) { + middlewares.push(logger); +
eyecatch
Mon, Oct 1, 2018

React + Reduxアプリケーションプロジェクトのテンプレートを作る ― その7: React Redux

ReactとReduxを学ぶために、開発環境というかプロジェクトテンプレートをスクラッチから作っている。 (最終的な成果はGitHubに置いた。) 前回はReduxをセットアップした。 (adsbygoogle = window.adsbygoogle || []).push({}); React Redux 前回はReduxをセットアップして、ActionをStoreにディスパッチしてstateを更新できるようになった。 今回はこれをReactにつなぐ。 使うのはReact Redux。 yarn add react-redux v5.0.7が入った。 Presentational Components と Container Components React Reduxの使い方を理解するには、Presentational Components と Container Components という概念を知らないといけない。 これはReactコンポーネントを役割別に分ける考え方で、それぞれ以下のような特徴をもつ。 Presentational Components Container Components 主な役割 DOMをレンダリングする データを取得したりstateを更新したりする(Reduxとつなぐ) Reduxとの関連 無し 有り データの読み込み propsから読む Reduxのstateオブジェクトから読む データの更新 propsで渡されたコールバックを呼ぶ ReduxのActionをディスパッチする 作り方 自前で書く React Reduxで生成する 要するに、普通にReactで作ったUIコンポーネントを、React Reduxで生成するContainer ComponentでラップしてやることでReduxのStoreとつなぐことができる。 connect() Container Componentの生成にはReact Reduxのconnect()というAPIを使う。 React Reduxを使う場合、Reduxのstateの更新に応じてReactコンポーネントに新しいpropsを渡して再レンダリングすることになるが、この新しいpropsを作ってコンポーネントに渡す処理を定義するのがconnect()。 connect()の第一引数には、ReduxのstateのプロパティとReactコンポーネントのpropsのプロパティとのマッピングをする関数であるmapStateToProps()を渡す。 mapStateToProps()はstateの更新に応じて呼び出され、引数にstate(と現在のprops)が渡される。 mapStateToProps()が返すオブジェクトはReactコンポーネントに渡されるpropsにマージされる。 connect()の第二引数には、Storeのdispatch()を呼び出す処理とReactコンポーネントのpropsのプロパティとのマッピングをする関数であるmapDispatchToProps()を渡す。 mapDispatchToProps()の引数にはdispatch()が渡される。 mapDispatchToProps()が返すオブジェクトはReactコンポーネントに渡されるpropsにマージされる。 (mapDispatchToProps()は第二引数にpropsを受け取ることもできて、この場合、propsの更新に反応して呼び出されるコールバックになる。) connect()を実行すると関数が返ってくる。 この関数にReactコンポーネント(Presentational Component)を渡して実行すると、Storeに接続されたReactコンポーネント(Container
eyecatch
Wed, Sep 26, 2018

React + Reduxアプリケーションプロジェクトのテンプレートを作る ― その6: Redux

ReactとReduxを学ぶために、開発環境というかプロジェクトテンプレートをスクラッチから作っている。 (最終的な成果はGitHubに置いた。) 前回はMaterial-UIをセットアップした。 (adsbygoogle = window.adsbygoogle || []).push({}); Reactの状態管理 Reactによるプログラミングをするとき、小さいUIコンポーネントをたくさん作って、それらを組み合わせてVirtual DOMツリーを作っておいて、そこにpropsをほうりこんでレンダリングする、という感じになる。 また、レンダリングした後はコンポーネントのstateをいじって状態を変化させる。 このpropsやstateの扱いをReactの状態管理という。 propsやstateを適当にアドホックに設定してると、結局jQuery使ってるのとそんなに変わらなくなって辛くなるので、Reactの開発元であるFacebookはFluxというアーキテクチャを提案している。 Fluxでは、単一の(またはドメイン毎くらいの単位の)オブジェクトでアプリケーション全体の状態(state)を表し、これをStoreに保持する。 ReactはStoreが保持するstateを受け取り、それをもとにViewをレンダリングする。 Viewに対するユーザの操作(など)はActionというオブジェクトで表現され、Dispatcherに渡され、Dispatcherに登録されたcallbackを通してstateを変化させる。 データが常に一方向に流れて見通しがよく、各コンポーネントの独立性が高いのが特徴。 各コンポーネントは、受け取ったデータをピュアに処理すればよく、リアクティブにファンクショナルに実装できる。 Redux Fluxの実装、というか発展形がRedux。 ReduxではFluxのDispatcher辺りがReducerに置き換わっている。 ReducerはActionと現在のstateから次のstateを計算する純粋関数。 また、ReduxからはViewが切り離されていて、Actionによってstateを更新する状態管理ライブラリの役割に徹している。 ReactコンポーネントのイベントハンドラからActionオブジェクトを生成したり、更新したstateをReactに渡したりするつなぎ目は、別途React Reduxというライブラリが担当する。 ReduxとReact Reduxについては、Qiitaの「たぶんこれが一番分かりやすいと思います React + Redux のフロー図解」という記事が分かりやすい。 今回はReduxを導入する。 yarn add redux Redux v4.0.0が入った。 以降、現時点で唯一のUIコンポーネントであるHOGEボタンの状態管理を実装してみる。 Action まずActionを実装する。 Actionオブジェクトはどんな形式でもいいけど、普通はFlux Standard Action(FSA)にする。 FSAは以下のプロパティを持つプレーンオブジェクト。 type: Action種別を示す文字列定数。必須。 payload: Actionの情報を示す任意の型の値。任意。 error: Actionがエラーを表すものかを示す boolean プロパティ。エラーなら true にして、payload にエラーオブジェクトをセットする。任意。 meta: その他の情報を入れる任意の型の値。任意。 Actionのコードは、Actionのtypeに入れる値を定義するactionTypes.jsと、Action Creator(i.e. Actionオブジェクトを生成する関数)を定義するactions.jsからなり、ともにsrc/actions/に置く。 HOGEボタンをクリックしたときのAction、HOGE_BUTTON_CLICKEDを定義してみる。 src/actions/actionTypes.js: export const HOGE_BUTTON_CLICKED = 'HOGE_BUTTON_CLICKED'; src/actions/actions.js: import { HOGE_BUTTON_CLICKED, } from './actionTypes'; export function hogeButtonClicked(payload) { return { type: HOGE_BUTTON_CLICKED, payload, }; } こんな感じ。 Reducer 次はReducer。 Reducerは、上記Action Creatorが生成するActionオブジェクトに対応して起動し、Store(後述)から現在のstateオブジェクトを受け取って、Actionオブジェクトのpayloadの値(など)に応じて新しいstateオブジェクトを作る。 Reducerを書く前に、stateオブジェクトの構造を設計しておくことが推奨されている。 UIコンポーネント毎にプロパティを分けて、コンポーネント構造と同様の階層構造にしておけばだいたいよさそう。 HOGEボタンに一つ、クリックしたかどうかの状態(clicked)を持たせるとすると、stateオブジェクトは以下のようになる。 { hoge: { clicked: false, }, } Reducerはピュアじゃないといけないので、内部で副作用を起こしてはいけない。 副作用とは、具体的には以下のようなもの。 引数で与えられたオブジェクトを変更する。 REST APIへのリクエストを送る。 (ログの出力も厳密には副作用なんだろうけど、それは許されてる気がする。) また、ピュアであるためには参照透過性を持たないといけなくて、つまり同じ引数に対しては同じ戻り値を返さないといけないので、内部でDate.now()とかMath.random()とかを呼ぶのもダメ。 Reducerのコードはsrc/reducers/に置く。 HOGE_BUTTON_CLICKEDが発生したら、hogeのclickedをtrueにするReducer(hoge())は以下の感じに書ける。 src/reducers/reducers.js: import { HOGE_BUTTON_CLICKED } from '../actions/actionTypes'; const initialState = { hoge: { clicked: false, }, }; export function hoge(state = initialState, action) { switch (action.type) { case HOGE_BUTTON_CLICKED: const newHoge = { hoge: { clicked: true, }, }; return Object.assign({}, state, newHoge); default: return state; } } hoge()のポイントはたくさんある。 stateとactionを引数に取る。前者が現在の状態を表すstateオブジェクトで、後者がActionオブジェクト。 戻り値は新しい状態を表すstateオブジェクト。 actionオブジェクトはどのActionを表すものかは分からないので、action.typeを見てHOGE_BUTTON_CLICKEDだけを処理するようにする。 知らないActionだったら(i.e.
eyecatch
Thu, Sep 6, 2018

React + Reduxアプリケーションプロジェクトのテンプレートを作る ― その5: Material-UIとWebフォント

ReactとReduxを学ぶために、開発環境というかプロジェクトテンプレートをスクラッチから作っている。 (最終的な成果はGitHubに置いた。) 前回はCSS周りの処理系をセットアップした。 (adsbygoogle = window.adsbygoogle || []).push({}); 既成Reactコンポーネント 前回まででHTMLもCSSもReactコンポーネント単位で書けるようになったんだけど、実際、自分で1からコンポーネントを書くのは、特にデザインセンスがない人にとっては辛い。 かっこいいUIコンポーネントを作りたいならデザイナーの協力が必要だけど、個人の開発などそれができない状況も多い。 という問題を抱えた人たち向けなのかはわからないが、既成のReactコンポーネントセットが色々OSSで提供されている。 Material-UI: GoogleのマテリアルデザインのReact実装。 Semantic UI React: Semantic UIのReactバインディング。 antd: Ant DesignのReact実装。 Blueprint: 複雑でデータ量の多いUI向けに作られたReact UIツールキット。 React-Bootstrap: BootstrapのReactバインディング。現時点ではv4未対応。 Grommet: HPEによるエンタープライズレディなデザインシステム。 Office UI Fabric React: OfficeなどのMicrosoft製品に使われているReactコンポーネントセット。 今回はこの中でも圧倒的に人気なMaterial-UIを導入する。 Material-UI Material-UIは簡単に使える。 とりあえずコアパッケージをインストールする。 yarn add @material-ui/core v1.4.1が入った。 あとはパッケージに入っている色々なコンポーネントをMaterial-UIのドキュメント見ながら使えばいいだけ。 import React from 'react'; import styled from 'styled-components'; +import Button from '@material-ui/core/Button'; const Wrapper = styled.div` font-size: 5rem; `; const App = () => ( <Wrapper> - HOGE + <Button variant="contained"> + HOGE + </Button> </Wrapper> ); export default App; これでただのテキストがボタンになった。 CSS Web Fonts 前節でいちおうMaterial-UI使えたけど、フォントをケアしてやるともう少しかっこよくなる。 Material-UIはRobotoフォントを想定して作られているが、これはブラウザにデフォルトで入ってはいないので、そのままだとArialとかにフォールバックされちゃう。 のでRobotoフォントを導入する。 フォントはCSS Web Fontsの機能である@font-faceで、フォントファイルをブラウザにロードさせることで導入できる。 @font-faceで読み込むフォントファイル(i.e.