ReactHooksの使い方について

この記事は[Nextremer Advent Calendar 2018](https://qiita.com/advent-calendar/2018/nextremer)の7日目の記事です。

(億番煎じぐらいですが)ReactHooks触ってみて使い方について検討したのでまとめます。
React Hooksが提供する機能の説明とかは他に良記事がたくさんあるので割愛。

# TL;DR
結局ReduxライクなReducerによる管理が楽そう。（ならそれReduxでよくね？とか言わない）
作ったものはこちら: [https://github.com/k-tada/react-hooks-todos](https://github.com/k-tada/react-hooks-todos)

# State管理について
Redux等を使わない、ReactのみでState管理する方法について検討してみた。
多分Redux使わなくなることは無いと思うけど、ReactでState管理する方が楽になるならそっちの方がいいので。

１Component内で完結するような小規模なStateの管理は今回の検討スコープから外してます。
> というか検討するまでも無く`useState`使えばOKかと。

## 基本はContext ＋ Reducer
ReactでコンポーネントをまたいでState管理をする場合はやっぱりContextを使うのが楽そう。
Stateの更新にはReactのuseReducerを使うのが良さそう。
ってことで、Context + Reducerでモジュールを管理するのが一番しっくり来るかなと思ったので実装してみた。
> どんどんReduxの設計に寄っていく。。。~~それReduxでよくね？~~

例えばTodoListの管理をするとして、作るならこんな感じになりそう

```javascript
const TodoContext = createContext({ todos: [] })

function Form() {
  const { dispatch } = useContext(TodoContext)
  // 略
  return (
    <div>
      <input type="text" ... onKeyUp={e => e.keycode==13 && dispatch( ... ) } />
    </div>
  );
}

function List() {
  const { state } = useContext(TodoContext)
  return (
    <div>
      {state.todos.map(todo => <div>{ todo }</div>)}
    </div>
  )
}

function Main() {
  const reducer = (state, action) => { /* 略 */ }
  const [state, dispatch] = useReducer(reducer, useContext(TodoContext))
  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      <Form />
      <List />
    </TodoContext.Provider>
  )
}
```

複数のContextを使う場合、Providerを重ねがけするイメージ。

```javascript
return (
  <HogeContext.Provider value={{ state, dispatch }}>
    <FugaContext.Provider value={{ state, dispatch }}>
      <App />
    </FugaContext.Provider>
  </HogeContext.Provider>
)
```

## Ducksパターン的なアレ
ここで、ContextごとにReducerを用意することになるので、Ducksパターンよろしく１ファイルにまとめちゃいたい。
ということでこうしてみた。

```javascript:stores/todos.js
import React, {
  createContext,
  useContext,
  useReducer,
} from "react"

export const Store = createContext({ todos: [] })

const reducer = (state, action) => {
  switch (action.type) {
    // 略
  }
}

export const createStore = () => {
  const [state, dispatch] = useReducer(reducer, useContext(Store))
  return { state, dispatch }
}
```

使う際はこんな感じ

```javascript:Main.js
import {
  Store as TodosStore,
  createStore as createTodosStore
} from './stores/todos';

function Main() {
  return (
    <TodosStore.Provider value={createTodosStore()}>
      <Form />
      <List />
    </TodosStore.Provider>
  );
}
```

```javascript:components/Form.js
import { Store as TodosStore } from "../stores/todos";

function Form() {
  const { dispatch } = useContext(TodosStore)
  // 略
  return (
    <div>
      <input type="text" ... onKeyUp={e => e.keycode==13 && dispatch( ... ) } />
    </div>
  );
}
```

```javascript:components/List.js
import { Store as TodosStore } from "../stores/todos";

function List() {
  const { state } = useContext(TodosStore)
  return (
    <div>
      {state.todos.map(todo => <div>{ todo }</div>)}
    </div>
  )
}
```

# まとめ
ReactHooksを使うと多少大きめのStateも扱いやすくなりそう。
ただReduxと併用すると確実に混乱する未来が見えるので、この仕組みを取るならMobXとかと併用する方がいいのかも知れない。

いずれにせよ、ReactHooks自体はまだRFCの段階で、これから破壊的な変更が無いとも限らないので、
しばらくはReactの動向を監視しつつ、従来のClass Componentやrecompose + SFCのまま運用するのが良さそう。