Help us understand the problem. What is going on with this article?

React + Flux入門

この記事ではReactとFluxのアーキテクチャの概要と、実際にReactとFluxを使ってアプリを開発する方法を簡単に説明します。特にFluxにフォーカスしています。

併せて、ReactとFluxを使ったアプリを簡単に作れるようにするstarter-react-fluxというツールもnpmに公開しました。使い方はこちらです。これを使えば、React + Flux + Babel + Webpack + ESlint + Prettier + Jest他ポピュラーなライブラリで構成されるPWAをすぐに生成できます。また、JavaScriptだけでなくTypeScriptにも対応しています。この記事は入門編なのでJavaScriptで説明しています。

Fluxとは

Facebookが提唱したUIを持ったアプリを作るためのアーキテクチャです。アーキテクチャを実現するための同名のライブラリもあります。

アーキテクチャとしてのFlux

Facebookは次のような図でFluxを説明しています。

In-Depth Overview

ざっくり言うと、View等でイベントが発生した結果Actionが生成されて、Dispatcher経由でStoreに渡り、Storeの内容が再度Viewに反映されるという図です。データの流れが一方通行(uni-dierctional)であることがFluxの重要な点です。

ただし、この説明だけだと、Actionは誰がどう作るのか、Storeが何なのかが明確ではありません。もう少しだけ詳しく説明すると次のようなものです。

  1. Viewが、Storeに格納された状態(state)を元に表示されます。
  2. Viewでイベント(onClickなど)が発生すると、対応するイベントハンドラ(例. handleClick())が呼ばれます。
    • Viewの例: React component
  3. イベントハンドラがAction Creatorという関数を呼び出します。
    • Action Creatorが行う処理の例: APIコール、DB操作、計算など
  4. Action Creatorは、Actionとよばれるデータ生成して、ActionDispatcherdisptach()メソッドに渡します。
    • Actionの例: REST APIのレスポンス、DBから取得したデータ
  5. StoreActionを受け取って、StoreのstateとActionの内容を元に、stateを更新します。
  6. Storeの状態(state)が、再びViewに反映されます。

ライブラリとしてのFlux

FacebookはFluxアーキテクチャを実現するためのライブラリをアーキテクチャと同じFluxという名前で提供しています。FacebookのFluxライブラリは、Fluxアーキテクチャに登場するDispatcher, Store, Viewに対応するコンポーネントを提供しています。

Fluxの要素名 対応ライライブラリ名
Dispatcher Dispatcher
Store ReduceStore
View Container, React Component

注: Fluxはv2.1.0からReduceStoreというStoreのコンポーネントとContainerというViewのコンポーネントが追加されました。これらは大変強力なコンポーネントで、ReduceStoreの状態をContainerに自動で反映してくれます。ReduceStore登場以前は、Storeの変更をViewに反映させるためにEvent Emitterが必要で記述が多く面倒でしたが、現在ではそのような処理は不要です。

Fluxの実装方法

基本的に以下のファイルを実装していきます。

  • ActionCreators: 何らかの処理を行い、結果からActionというデータを作り出して、Dispatcherに渡します。
  • Dispatcher: 渡されたActionをStoreに配信するHub的な役割です。Actionに関する統一的な処理を行う際などにコードを追加する事はありますが、普段はあまり気にすることはないかもしれません。
  • ReduceStore: アプリの状態を管理する非永続的なデータストアです。現在のストアの状態と新たなActionを元に新たな状態を作ります。
  • Component: 単なるReactコンポーネントです。
  • Container: ReduceStoreのデータを自動で受け取るだけの形式的なReact Componentです。アプリのロジックを書くことは推奨されていません。

Fluxの単方向データフローのデータ観点でこれを整理すると以下のような流れです。

React Component 
---[ event  ]---> ActionCreator
---[ action ]---> Dispatcher 
---[ action ]---> ReduceStore 
---[ state  ]---> Container 
---[ props  ]---> React Component 

さて、次はFluxの具体的な実装方法を要素別に見ていきます。

Action

  1. ActionCreatorsのファイルを作成します。
  2. ユーザイベントに対応するロジックやビジネスロジックを記述します。
  3. ロジックの処理結果を元にActionを作成してDispatcherに渡します。

用語の整理

Fluxを説明するドキュメントには、Action, Action Creator, Action Creator*s*とAction関連で似た言葉が出てきてわかり辛いので解説します。

Action

  • ユニークな識別子とデータから構成されるオブジェクトです。

    例:

    {
       type: "ユニークな識別子",
       payload: データ(APIのレスポンス等)
    }
    

    Actionのスキーマは規定されていませんが、自由だと不便なのでFlux Standard ActionというActionのスキーマ仕様が提案されています。starter-react-fluxでもFlux Standard Actionを採用しました。

  • ActionはDispatcher経由でStoreに渡されます。

  • ActionCreator: Actionを生成して、ActionをDispatcherに渡す関数です。ViewのonClick等から呼び出されるケースが多いです。

  • ActionCreators: 複数のActionCreatorが定義されたファイルです。

実例

import AppDispatcher from '../AppDispatcher';

const NewsActionCreators = {   //ActionCreators
  fetchNews(arg1, arg2) {      //ActionCreator

    //ユーザイベント等に対応するロジックを記述

    AppDispatcher.dispatch({   //Actionオブジェクト
      type: 'ACTION_TYPE_001',  //Actionを識別する一意の文字列, 
      payload: response,          //ロジックの結果をいれる
    });
  },

};

export default NewsActionCreators;

GitHub上の実装例

Store: ReduceStore

アプリケーションで使う状態(state)を管理します。

  1. ReduceStoreを拡張したクラスを作成します。
  2. stateの初期値を、getInitialState()関数の戻り値として定義します。
    • 例: Map, Array, String, Number, Object,..
    • 例: blogの記事一覧を格納するReduceStore -> blog記事を古い順に格納したArray
  3. 「現在のstate」と「dispatcher経由で受け取ったaction」から「新たなstate」を作成するreduce(state, action)を実装します。
    • 例: blogのコメント一覧の場合、「現在のコメント一覧(Array)」とActionオブジェクト内の「新たなコメント一覧(Array)」を結合したArrayをreduceでreturn。

実例

import { ReduceStore } from 'flux/utils';
import AppDispatcher from '../dispatchers/AppDispatcher';

class NewsStore extends ReduceStore {
  getInitialState() {
    return []; //stateの初期値を定義
  }

  reduce(state, action) {
    switch (action.type) {
      case 'ACTION_TYPE_001':
        //actionの内容を新たなstateとする (action.dataはarrayと仮定)
        return action.payload; 
      case 'ACTION_TYPE_002':
        //現在のstateとactionの内容から新たなstateを作成
        return state.concat(action.data);
      default:
        //現在のstateをそのまま返す
        return state;
    }
  }
}

export default new NewsStore(AppDispatcher);

GitHub上の実装例

※ FacebookのFlux(flux/utils)では、Storeを実現するクラスとしてStore, ReduceStore, MapStoreの3種類を用意していますが、Storeは煩雑でMapStoreはv3.0.0で削除されたのでReduceStoreだけを考えれば良いです。

View: Container

ReactComponentの一種です。ただし、ReduceStoreのstateの内容を取得して配下のReactコンポーネントに渡すだけの特殊なReact Componentです。UIやロジックをContainerの中で実装するのは非推奨で、UIやロジックは子供のReactComponentで実装します。

  1. ReactComponentを作成して、Container.create()でContainer化します。
  2. getStores()で、利用したいReduceStoreを指定します。
  3. calculateState()で、ReduceStoreからstateを取得して、Conttainerで使う際のスキーマを定義します。

実例

import React, { Component } from 'react';
import { render } from 'react-dom';
import { Container } from 'flux/utils';
import NewsStore from './stores/NewsStore';
import FavStore from './stores/FavStore';

class _App extends Component {
  static getStores() {
    return [NewsStore, FavStore]; //利用したいReduceStore
  }

  static calculateState() {
    return { //container内で`this.state.KEY_NAME`でアクセス可能
      news: NewsStore.getState(),
      favs: FavStore.getState(),
    };
  }

  render() {
    //通常のReactComponentのように書く
  }
}

const AppContainer = Container.create(_App);
render(<AppContainer />, document.getElementById('root'));

GitHub上の実装例

View: React Component

普通のReact componentです。Containerのstateをpropsとして受け取ります。

付録: starter-react-fluxについて

更新履歴

  • v4: 2019/3

    • Progressive Web Application (PWA)に対応
    • UIアップデート
  • v5: 2019/8

    • JavaScriptとTypeScriptの両方に対応
      • --tsオプションをつけるとTypeScriptのプロジェクトを生成。(標準はJavaScript)
    • npmとyarnの両方に対応
      • --yarnオプションをつけるとyarnでライブラリをインストール。(標準はnpm)

使い方

  1. React/Fluxアプリを作ります。

    JavaScriptの場合

    npx starter-react-flux init
    

    TypeScriptの場合

    npx starter-react-flux init --ts
    
  2. 作成したReact+Fluxアプリを起動します。

    npm start
    
- アプリが起動します。
- ファイルを修正すると自動でリロードされます。

![Screen Shot](https://raw.githubusercontent.com/SokichiFujita/starter-react-flux/master/images/app1.png)
  1. ESlintで静的解析を行います。

    npm run lint
    
  2. PrettierでESlintのルールを満たすようコードをある程度自動修正します。

    npm run lint
    
  3. Jestでテストを実行します。

    npm test
    
  4. アプリをビルドします。ビルドしたアプリはpublic配下に格納されます。

    npm run build
    
Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away