FluxによるReactアプリの状態管理 ReactHooks編

※ ReducerとVIEWコンポーネントの接続方法に関する記事を追加しました。

ReactHooks学習用にTodoリストを作成した際のまとめです。
[React](https://ja.reactjs.org/)の基本事項は既に習得されている方向けです。
[ReactのJSX記法](https://ja.reactjs.org/docs/introducing-jsx.html)を利用する為、[Babel](https://babeljs.io/)などのトランスパイラを利用します。
[webpack](https://webpack.js.org/)などのモジュールバンドラーの利用も前提としています。
今回は、[Parcel](https://parceljs.org/)を利用しています。

# Fluxとは
Reactによるアプリケーション構築を保管する為に考案された、設計パターンです。
データの流れを一方通行にすること、アプリケーションの状態「State」を一元的に管理することで、アプリケーションの管理が煩雑化することを防ぐ試みです。
Fluxによるアプリケーションは、下記のパートで構成されます。

- Dispatcher： アプリケーションへの更新情報を集約・通知
- Store： アプリケーションの状態を保持・管理
- Action: アプリケーションの更新情報を得る為の内部API　
- View： Reactコンポーネントによるインターフェース

Dispatcherは、アプリケーションに1つだけ存在します。
Storeは、アプリケーションの規模などに応じて1つ以上存在します。
Actionは、アプリケーションに発生しうる変更の数だけ存在します。
Vewは、Storeと接続したコンポーネントを頂点としてツリー構造を形成します。

詳細はこちらの記事で扱っております。
[FluxによるReactアプリの状態管理　Flux・FluxUtils編](https://qiita.com/koedamon/items/6bca2375feec89c66359)

本家サイトはこちら。
[Flux](https://facebook.github.io/flux/)

このFluxを実装する為のフレームワークとして、公式より[Flux Utils](https://facebook.github.io/flux/docs/dispatcher.html)が提供されています。
しかし、React本体に導入されたHooksだけで、Fluxの実装が可能になりました。

# Hooksとは
ReactのVer.16.8で新たに導入された機能群です。
Class構文を利用せずに、シンプルな関数の組み合わせでアプリケーションを構築することを可能としてくれます。
2019年9月時点で、10個の組み込みHookが提供されています。
Reactのパッケージに含まれており、共通して名前が`use-`で始まる関数です。
その他、自分で[カスタムHook](https://ja.reactjs.org/docs/hooks-custom.html)を作成することも可能です。

```js
import React { useState, useEffect } from 'react';
```

[Hooks | React](https://ja.reactjs.org/docs/hooks-intro.html)

# Todoアプリ
[ReduxのベーシックチュートリアルにあるTodoサンプル](https://redux.js.org/basics/example)をベースにしてみました。
Hooksを使った構築は、Flux思想を忠実に再現したFluxUtilsを使用して構築した場合と比較すると、Reduxに幾分か寄っている印象です。
その為、Redux・React-Reduxで構築した場合の構造をだいたい維持したまま、Hooksに置き換えてみました。

このRedux・React-ReduxのTodoアプリについてはこちらで扱っております。
[FluxによるReactアプリの状態管理　Redux・React-Redux編](https://qiita.com/koedamon/items/fe24f17374dc1d24c7fe)

この記事で作成するTodoアプリの完全なソースコードはこちらです。
[todoapp-flux-hooks](https://github.com/BeeBow6/todoapp-flux-hooks)
[動作のサンプル](https://beebow6.github.io/todoapp-flux-hooks/dist/)

# Actions
Actionは、Reducerに対して送信するデータです。
実体はJavaScriptのオブジェクトです。
Actionの種類を示す、`type`プロパティを必ず持っています。`type`プロパティの値は、一般的に文字列が用いられます。
アプリケーションの規模が大きい場合は、予め定数として定義しておくことが望ましいでしょう。
その他Reducerに送信したいデータが含まれます。

```js:新規Todoの追加時のAction
// ActionType
const ADD_TODO = 'ADD_TODO'

// Action
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}
```

このActionを生成するのがActionCreatorです。
Fluxの基本では、Actionの生成から送信までをActionCreator内で実行することになっています。
今回はReduxと同じく、生成して返すのみです。

```js
// Actionの識別子
export const TodoActionTypes = {
  ADD_TODO: 'ADD_TODO',
  /* 略 */
};
// TodoID
let nextTodoId = 0;
/**
 * Todo項目の追加
 * @param {String} text Todoテキスト
 * @returns {Object} Todo追加用action
 */
export const addTodo = (text) => ({
  type: TodoActionTypes.ADD_TODO,
  id: nextTodoId++,
  text
});
```

# アプリケーションのStateの設計
アプリケーションの状態を一つのオブジェクトで表す「State」を設計します。
今回のTodoアプリのStateは、以下のようになります。

```js
{
  visibilityFilter: 'SHOW_ALL',
  todos: [
    {
      id: 1,
      text: 'Write a article about Redux',
      completed: true
    },
    {
      id: 2,
      text: 'Go to the Cats cafee',
      completed: false
    }
  ]
}
```

visibilityFilterの指定には、予め用意した定数を用います。

```js:actions.js
export const VisibilityFilters = {
  SHOW_ALL: 'SHOW_ALL',
  SHOW_COMPLETED: 'SHOW_COMPLETED',
  SHOW_ACTIVE: 'SHOW_ACTIVE'
};
```

# Reducers
## Reducerとは
Reducerは、本家FluxではなくReduxで登場するモジュールです。
FluxのStoreの、Stateの変更をする責務を切り出したものです。
これは予めツールなどが用意されているわけではなく、一定のルールに基づいて自分で構築する関数です。
React側では特に指示がないので、Redux側のルールに基づいて作成することとします。

Reducerは、[副作用](https://ja.wikipedia.org/wiki/%E5%89%AF%E4%BD%9C%E7%94%A8_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0))のない純粋な関数です。
以前のStateと新たに生成されたActionを受け取り、新しいStateを返します。

```js
(previousState, action) => newState
```

`previousState`と`action`の内容が同一であれば、何度呼び出しても同じ`newState`を返します。
その為、Reducerには下記のルールがあります。

- 引数を変更してはいけません。
- APIの呼び出しやルーティングなどの、副作用が発生する処理を記述してはいけません。
- 純粋でない関数を呼び出してはいけません。（例：Date.now(), Math.random()）

```js
function todoApp(previousState, action){
  switch(action.type){
    case 'TYPE_A':
      return newState;
    case 'TYPE_B':
      return newState;
    default:
      return previousState;
  }
}
```

ActionTypeでActionの種類を識別し、`previousState`と`action`の情報を基に新しいStateを生成します。
初期呼び出し時は、`previousState`は`undefined`が渡される為、[デフォルト引数](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters)には、Stateの初期値を設定しています。

Reduxでは、`state`の初期値をReducerの[デフォルト引数](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Default_parameters)として用意します。
しかし、Hooksを利用する場合は不要になります。

## Reducerの分割
アプリケーションの規模が大きい場合、StateおよびReducerのサイズも大きくなってきます。
その場合、一つのReducerで管理することが難しくなる為、適宜分割することが望ましいでしょう。

`todos`と`visibilityFilter`、それぞれ管理するReducerを作成します。

```js:reducers/todos.js
const todos = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return // Todo項目追加後newState
    case 'TOGGLE_TODO':
      return // Todo項目切替後newState
    default:
      return state;
  }
};

export default todos;
```

```js:reducers/visibilityFilter.js
const visibilityFilter = (state, action) => {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter;
    default:
      return state;
  }
};

export default visibilityFilter;
```

## Reducerの結合
関数を分割したものの、最終的に必要なStateは一つのオブジェクトです。
その為、複数のReducerの結果をまとめるReducerが必要です。

```js：reducers/index.js
import todos from './todos';
import visibilityFilter from './visibilityFilter';

const rootReducers = (state = {}, action = {}) => {
  return {
    todos: todos(state.todos, action),
    visibilityFilter: visibilityFilter(state.visibilityFilter, action)
  };
};

export default rootReducers;
```

Reduxでは複数のReducerを束ねる為の[combineReducers()](https://redux.js.org/api/combinereducers)関数が提供されています。
おおよそ下記のようなコードです。

```js
/**
 * Reducerをまとめるヘルパー関数
 * @param {Object} reducers reducerの連想配列
 * @returns {Function} 結合後のReducer関数
 * 
 * 参考: https://github.com/reduxjs/redux/blob/master/src/combineReducers.js
 */
const combineReducer = (reducers) => {
  const reducerKeys = Object.keys(reducers);
  const finalReducers = {};

  for (let key of reducerKeys) {
    const reducer = reducers[key];
    if (typeof reducer === 'function') {
      finalReducers[key] = reducer;
    }
  }

  const finalReducerKeys = Object.keys(finalReducers);

  return (state, action) => {
    let newState = {};
    let hasChanged = false;

    for (let i = 0; i < finalReducerKeys.length; i++) {
      const key = finalReducerKeys[i];
      const reducer = finalReducers[key];
      const prevState = state[key];
      const nextState = reducer(prevState, action);

      if (typeof nextState === 'undefined') {
        throw new Error(`「 ${key} 」は存在しないStateです。`)
      }
      newState[key] = nextState;
      hasChanged = hasChanged || (nextState !== prevState);
    }

    return hasChanged ? newState : state;
  };
};

// 利用
export default combineReducer({
  todos,
  visibilityFilter
});
```

# useReducerを利用したFluxの構築
Fluxは基本的に、Viewのコンポーネントの外で一元的にアプリケーションの状態を管理する仕組みです。
FluxUtilsではDispatcherとStoreが、ReduxではStoreとReducerというViewから独立したモジュールがこの責務を負っていました。
責務の微妙な違いはあれどStateを保持する「Store」は両方にあります。
Hooksを利用した場合、Stateの保持はView側のルートコンポーネントが担当することになるようです。
実装にはHooksの組み込み関数「useReducer」を利用します。

```js
import React, { useReducer } from 'react';
import Reducer from '../reducers';
import App from '../components/';

export default function(){
  const [state, dispatch] = useReducer(Reducer, initialState);

  return (
    <App state={state} dispatch={dispatch} />
  );
}
```

`useReducer()`の第一引数にReducerを渡します。第二引数はStateの初期値です。
戻り値として、Viewで保持するStateと、State更新の為のdispatch関数を受け取ります。
以降、このdispatch関数の引数にActionを渡すと、Reducerが実行され、新たなstateがViewに渡されるようになります。

FluxUtilsやReact-Reduxでは、ViewにStateを引き渡す前に以前のStateと新しいStateの比較を行っていました。
useReducerを利用した場合も同様の比較確認が行われ、変更を検知できた場合に再レンダリングが発生します。

[useReducer | React](https://ja.reactjs.org/docs/hooks-reference.html#usereducer)

# ContextとuseContext
ルートコンポーネントで保持されているStateは、基本気にpropsを通じて下位コンポーネントへと共有されます。
その階層がふかくなると、長いバケツリレーが発生してしまいます。
Ver.16.3で導入された「Context」を利用すると、下位コンポーネントからも等しくStateにアクセスできるようになります。

[Context | React](https://ja.reactjs.org/docs/context.html)

## 利用準備
Contextオブジェクトを生成して公開します。
アプリケーション内で複数作成することが可能です。

```js:myContext.js
import React from 'react';

const MyContext = React.createContext(defaultValue);
export default MyContext;

// 初期値defaultValueは省略可
```

[React.createContext | React](https://ja.reactjs.org/docs/context.html#reactcreatecontext)

## データの提供
Contextオブジェクトに付属するProviderコンポーネントを利用して、提供したいデータを登録します。

```jsx
import MyContext from './myContext';
import RootApp from './rootApp';

function App(){
  // valueプロパティにに渡す
  return (
    <MyContext.Provider value={AppStateData}>
      <RootApp />
    </MyContext.Provider>
  );
}
```

[Context.Provider | React](https://ja.reactjs.org/docs/context.html#contextprovider)

## データの読み取り
Providerを通して提供されたデータを、下位のコンポーネントが直接受け取るには、2種類の方法があります。

### Consumerコンポーネント
Contextオブジェクトに付属するConsumerコンポーネントを利用して、Providerで提供されたデータにアクセスします。
Consumerコンポーネントにコールバック関数を渡すと、引数でProviderのvalueプロパティに渡されたデータを受け取れます。

```jsx
import MyContext from './myContext';

function SomeContainer(){
  return (
    <MyContext.Consumer>
      {({ param, callBack }) => {
        <button onClick={callBack}>
          {param}
        </button>
      }}
    </MyContext.Consumer>
  );
}
```

[Context.Consumer | React](https://ja.reactjs.org/docs/context.html#contextconsumer)

### useContext
useContextを利用すると、Consumerコンポーネントよりもすっきりと記述できます。
useContextの引数にContextオブジェクトを渡すと、戻り値としてStateを受け取れます。

```jsx
import React, { useContext } from 'react';
import MyContext from './myContext';

function SomeContainer(){
  const { param, callBack } = useContext(MyContext);
  return (
    <button onClick={callBack}>
      {param}
    </button>
  );
}
```

[useContext | React](https://ja.reactjs.org/docs/hooks-reference.html#usecontext)

# PresentationalComponents と ContainerComponents
ReactとReduxでアプリケーションを構築する際は、基本的にコンポーネントをPresentationalComponentとContainerComponentの2種に分けます。
PresentationalComponentはビジネスロジックを持たず、描画に徹します。
ContainerComponentはReduxと接点を持ち、PresentationalComponentに必要なpropsを受け渡します。
こうすることで、より見通しの良いアプリケーションの構築を可能とし、コンポーネントの再利用性を高めます。

Reducerの時と同様、View側もReduxのルールを取り入れて構築することとします。

[Presentational and Container Components | Redux](https://redux.js.org/basics/usage-with-react#presentational-and-container-components)

## ルートコンポーネントの場合
ルートコンポーネントでは、useReducerを用いてReducerと接続しつつ、Contextで下位コンポーネントへデータを提供する仕組みを準備します。

```jsx
import React, { useReducer } from 'react';
import TodoContext from '../context';
import TodoReducer from '../reducers';
import TodoApp from '../components/';
import { VisibilityFilters } from '../actions';

const initialState = {
  visibilityFilter: VisibilityFilters.SHOW_ALL,
  todos: []
};

const App = () => {

  const [state, dispatch] = useReducer(TodoReducer, initialState);
  const value = { ...state, dispatch };

  return (
    <TodoContext.Provider value={value}>
      <TodoApp />
    </TodoContext.Provider>
  );
};

export default App;
```

useReducerを実行して得られた`state`と`dispatch`を、Providerコンポーネントへ渡します。
その際、stateとdispatchをまとめたオブジェクトを作成してvalueプロパティに渡しています。

Contextを利用するに当たり、この毎回オブジェクトを作成して渡すというのはパフォーマンス上、あまりよろしくないそうです。
Providerを含むコンポーネントが再レンダリングされる度に、Providerに渡しているオブジェクトが新しくなってしまうため、`value`に渡しているStateに変更が無くとも関連するConsumerを含むコンポーネントの再レンダリングが発生してしまいます。
上記の例では、Stateの更新以外に再レンダリングが発生する要因が無いので、問題ないかなと思っています。

[コンテクスト注意事項 | React](https://ja.reactjs.org/docs/context.html#caveats)

## TodoListの場合
Todoリストを表示するコンポーネントのツリーは下記のとおりです。
VisibleTodoListがContainerComponentsです。

```
Provider
  └ VisibleTodoList ☆
      └ TodoList
          └ Todo
```

useContextを使ってStateから必要な項目を取得します。
todoリストの配列は現在のフィルターにマッチするものを抽出して、下位コンポーネントに渡します。
また、項目をクリック時に完了状況を切り替えるためのコールバック関数も定義しています。

```jsx
import React, { useContext } from 'react';
import TodoContext from '../context';
import TodoList from '../components/todoList';
import {
  toggleTodo,
  VisibilityFilters
} from '../actions';


const VisibleTodoList = () => {

  const { todos, visibilityFilter, dispatch } = useContext(TodoContext);
  let visibleTodos;

  switch (visibilityFilter) {
    case VisibilityFilters.SHOW_ALL:
      visibleTodos = todos;
      break;
    case VisibilityFilters.SHOW_COMPLETED:
      visibleTodos = todos.filter(t => t.completed);
      break;
    case VisibilityFilters.SHOW_ACTIVE:
      visibleTodos = todos.filter(t => !t.completed);
      break;
    default:
      throw new Error('Unknown filter ' + visibilityFilter);
  }

  return (
    <TodoList
      todos={visibleTodos}
      toggleTodo={id => { dispatch(toggleTodo(id)) }}
    />
  );
};

export default VisibleTodoList;
```

## AddTodoの場合
Todoに項目を追加するコンポーネントを構築します。
Form要素を含むコンポーネントなど、責務の分離が難しい場合は、分離しないという選択肢もあります。

```jsx
import React, { useState, useContext } from 'react';
import TodoContext from '../context';
import { addTodo } from '../actions';

const AddTodo = () => {

  const [inputText, setInputText] = useState('');
  const { dispatch } = useContext(TodoContext);

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault();
          if (!inputText.trim()) {
            return;
          }
          dispatch(addTodo(inputText));
          setInputText('');
        }}
      >
        <input
          value={inputText}
          onChange={e => setInputText(e.target.value)}
        />
        <button type="submit">Add Todo</button>
      </form>
    </div>
  );
};

export default AddTodo;
```

### useContext
関数コンポーネント内で、ローカルなStateを持つ為のHookです。
inputフォームの値を保持する為に利用しています。

```js
const [state, setState] = useState(initialState);
```

[useState | React](https://ja.reactjs.org/docs/hooks-reference.html#usestate)

# イメージ図
自分なりにA4にまとめてみました。
![TodoList_Hooks.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362737/811c9fbc-5db5-4f3c-f679-7d45f1b83ee6.png)

# 2019年6月30日追記
Reduxのからの流れで当たり前の様にReducerを一つにまとめていたのですが、ReactのReducerは必ずしも一つにまとめる必要はなさそうです。
下記の記事を参考にいたしました。
[How to use useReducer in React Hooks for performance optimization](https://medium.com/crowdbotics/how-to-use-usereducer-in-react-hooks-for-performance-optimization-ecafca9e7bf5)

## useReducerを複数利用する
わざわざ`combineReducer`でまとめなくても、FluxのStoreがそうであるように、ReactのReducerも管理単位ごとに複数存在させます。
それに応じて接点を提供するuseReducerも複数利用します。

```jsx
import React, { useReducer } from 'react';
import TodoContext from '../context';
import {
  TodosReducer,
  FilterReducer
} from '../reducers';
import TodoApp from '../components/';
import { VisibilityFilters } from '../actions';

const App = () => {

  const [todos, dispatchTodo] = useReducer(TodosReducer, []);
  const [visibilityFilter, dispatchFilter] = useReducer(FilterReducer, VisibilityFilters.SHOW_ALL);

  const value = {
    todos,
    visibilityFilter,
    dispatchTodo,
    dispatchFilter
  };

  return (
    <TodoContext.Provider value={value}>
      <TodoApp />
    </TodoContext.Provider>
  );
};

export default App;
```

本記事はあくまでFluxの記事なので`useReducer`によるReducerとの接点をVIEWのルートコンポーネントにしか置いていませんが、`useReducer`およびReducerは、個々のコンポーネントのローカルStateの管理に用いることも、もちろん可能です。

```jsx
// 一つの関数コンポーネント内でstateの管理が煩雑化した場合に、外部化させる
const localReducer = (state, action) => {
  switch (action.type) {
    case 'hoge':
      return {
        ...state,
        hoge: action.value
      };
    // ...
  }
}

const SomeComponent = (props) => {
  const [state, dispatch] = useReducer(localReducer, initialState);

  const onClick = (value) => {
    dispatch({
      type: 'hoge',
      value
    });
  };
  // ...
}
```

[useYourImagination() | React](https://ja.reactjs.org/docs/hooks-custom.html#useyourimagination)

## Reducer毎にContextを用意する
ContextもReducer毎に用意して、もうちょっとルートコンポーネントにおける接続部分をすっきりさせてみます。

### Hooksを利用した新Store
TodosのReducerとContextを、useReducerを使ってひとまとめにします。

```jsx
import React, { useReducer, useContext } from 'react';
import { TodoActionTypes } from '../actions';

const Context = React.createContext();

// Reducer
const reducer = (state, action) => {
  switch (action.type) {
    case TodoActionTypes.ADD_TODO:
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case TodoActionTypes.TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
};

// Todos専用Provider
const TodosProvider = ({ children }) => {
  const contextValue = useReducer(reducer, []);

  return (
    <Context.Provider value={contextValue}>
      {children}
    </Context.Provider>
  );
};

// TodosのstateとDispatcherを提供するカスタムHook
const useTodos = () => {
  const contextValue = useContext(Context);
  return contextValue;
}

export { TodosProvider, useTodos };
```

`useTodos`はカスタムHookと呼ばれる、ユーザが独自に定義できるHookです。
内部でビルトインHooksを利用しつつも、Reactコンポーネントは返しません。
VisibilityFilterも同様にひとまとめにします。
ルートコンポーネントでは、各Storeから提供されている`Provider`を設置するだけです。
ロジックが無くなってすっきりしたので、VIEWをまとめる為だけの中間コンポーネントは廃止しました。

```jsx
import React from 'react';
import {
  TodosProvider,
  FilterProvider
} from '../stores';
import AddTodo from './addTodo';
import VisibleTodoList from './visibleTodoList';
import Footer from '../components/footer';


const App = () => {

  return (
    <FilterProvider>
      <TodosProvider>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
      </TodosProvider>
    </FilterProvider>
  );
};

export default App;
```

下位のContainerコンポーネントでは、各Storeから提供されているカスタムHookを利用します。
カスタムHookもビルトインHookと同様、コンポーネントのトップレベルのスコープで実行する必要があります。

```jsx
import React from 'react';
import {
  useTodos,
  useFilter
} from '../stores';
import {
  VisibilityFilters,
  toggleTodo
} from '../actions';
import TodoList from '../components/todoList';

const VisibleTodoList = () => {

  // useReducer利用時と同じ要領で分割代入
  const [todos, dispatch] = useTodos();
  const [visibilityFilter] = useFilter();

  let visibleTodos;

  switch (visibilityFilter) {
    case VisibilityFilters.SHOW_ALL:
      visibleTodos = todos;
      break;
    case VisibilityFilters.SHOW_COMPLETED:
      visibleTodos = todos.filter(t => t.completed);
      break;
    case VisibilityFilters.SHOW_ACTIVE:
      visibleTodos = todos.filter(t => !t.completed);
      break;
    default:
      throw new Error('Unknown filter ' + visibilityFilter);
  }

  return (
    <TodoList
      todos={visibleTodos}
      toggleTodo={id => { dispatch(toggleTodo(id)) }}
    />
  );
};

export default VisibleTodoList;
```

Providerコンポーネントは、必ずしもルートに全部まとめる必要は無いかと思います。
ここまでのコードはこちらにあります。
[multiple_store | github](https://github.com/BeeBow6/todoapp-flux-hooks/tree/multiple_store)

[フックのルール](https://ja.reactjs.org/docs/hooks-rules.html)
[カスタムフックの作成 | React](https://ja.reactjs.org/docs/hooks-custom.html)

### ReduxとContextの接続ツール
`todosStore`と`visibilityFilterStore`で同じ事をやっているので、まとめられるのでは？と思ってやってみました。

```jsx:connect.jsx
import React, { useReducer, useContext } from 'react';

/**
 * ReducerとContextを接続する
 * @param {Function} reducer 対象Reducer
 * @param {*} initialState state初期値
 */
const connect = (reducer, initialState) => {

  const Context = React.createContext();

  const Provider = ({ children }) => {
    const contextValue = useReducer(reducer, initialState);

    return (
      <Context.Provider value={contextValue}>
        {children}
      </Context.Provider>
    );
  };
  Provider.displayName = reducer.name && (reducer.name !== 'anonymous') ? `Provider(${reducer.name})` : 'Provider';

  const useConsumer = () => {
    const contextValue = useContext(Context);
    return contextValue;
  };

  return { Provider, useConsumer };
};

export default connect;
```

ここで登場する`displayName`は、JavaScriptの非推奨プロパティの[displayName](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/displayName)ではなく、Reactコンポーネントのプロパティです。
[デバッグツール](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja)利用時に表示される名前を指定するのに利用します。

[displayName | React](https://reactjs.org/docs/react-component.html#displayname)

#### Reducerとの接続

```js
import connect from './connect';

// Reducer
const todos = (state, action) => {
  switch (action.type) {
    case TodoActionTypes.ADD_TODO:
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ];
    case TodoActionTypes.TOGGLE_TODO:
      return state.map(todo =>
        todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
      );
    default:
      return state;
  }
};

// Reducerと初期値を渡すのみ
export default connect(todos, []);
```

#### Providerの利用

```jsx
import TodosStore from '../stores/todoStore';
import FilterStore from '../stores/visibilityFilterStore';

const App = () => {

  return (
    <FilterStore.Provider>
      <TodosStore.Provider>
        <AddTodo />
        <VisibleTodoList />
        <Footer />
      </TodosStore.Provider>
    </FilterStore.Provider>
  );
};
```

#### Consumerの利用
`React.useState`という記述方法に倣ってみました。
ただ、[eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks)ではHooksと認識されませんでした。
一応これで無事動いてはいるようです。ご参考までに……。

```jsx
import TodosStore from '../stores/todoStore';
import FilterStore from '../stores/visibilityFilterStore';

const VisibleTodoList = () => {

  const [todos, dispatch] = TodosStore.useConsumer();
  const [visibilityFilter] = FilterStore.useConsumer();

  let visibleTodos;

  switch (visibilityFilter) {
    case VisibilityFilters.SHOW_ALL:
      visibleTodos = todos;
      break;
    case VisibilityFilters.SHOW_COMPLETED:
      visibleTodos = todos.filter(t => t.completed);
      break;
    case VisibilityFilters.SHOW_ACTIVE:
      visibleTodos = todos.filter(t => !t.completed);
      break;
    default:
      throw new Error('Unknown filter ' + visibilityFilter);
  }

  return (
    <TodoList
      todos={visibleTodos}
      toggleTodo={id => { dispatch(toggleTodo(id)) }}
    />
  );
};
```

# 参考情報
[React HooksとContextAPIでFluxをやってみる](http://tech.connehito.com/entry/react-hooks-with-flux)
[Redux, Flux, and the React Hooks API, Which should I use?](https://medium.com/@JeffLombardJr/redux-flux-and-the-react-hooks-api-5add9257826b)