React/Reduxを約三年間書き続けてきたので知見を共有します

[Enigmo Advent Calendar 2018](https://qiita.com/advent-calendar/2018/enigmo)の4日目の記事です。

# この記事の目的
Enigmoが運営している[BUYMA](https://www.buyma.com/)では古代から運用しているjQueryの他に、2016年頃から一部ページのフロントエンドをReact/Reduxで構築しています。
私自身もEnigmoに入社してからの約三年間でReact/Reduxアプリケーションの開発に多数携わってきましたので、そこで培った知見を共有したいと思います。

# React/Reduxの利点
まずはじめに、ReactとReduxを使うメリットを再確認しておきたいと思います。
それぞれのメリットをしっかりと認識しておくことで、実装する際どう書くか迷ってしまった場合などにそのメリットを最大限活かす選択をすることができます。

## Reactの利点
- コンポーネント化が容易で再利用性が高い
- 状態をDOMから分離できる(Stateless)
    - Reduxのような外部ライブラリを併用するとコンポーネント自体からも分離できる
    - **(props) => DOM Tree** という関数のように扱える(同じinputなら常に同じoutputを得られる)
- Propsとしてイベントハンドラを渡していくアーキテクチャなので、ロジックをまとめて疎結合にしやすい

## Reduxの利点
- Reducer、ActionCreator、Componentといった利用側が書くべきモジュールはすべて純粋な関数で書ける
    - ほとんど関数の集合だけでアプリケーションが完成する
- 副作用のある処理はすべて後述するMiddleware層に持っていける
- 上記の理由からテストが非常に書きやすい

# React/Reduxはこう書く！！
では実際に私がReact/Reduxアプリケーションを実装する際指標としているプラクティスを解説していきたいと思います。

## Container Componentsの分割
`Container Components` とは` Redux Store` と `connect` しているコンポーネントのことです。
[詳細はReduxの公式Doc](https://redux.js.org/basics/usagewithreact#implementing-container-components)をご確認ください。

Container Componentsを適切に分割することでコードの見通しを良くします。
Containerの中にContainerが存在する、 **Container in Container** も許容します。

### 分割されていない例

```BadContainer.jsx
import { connect } from 'react-redux'
import * as React from 'react'
import ComponentA from '../components/ComponentA'
import ComponentB from '../components/ComponentB'
import * as actions from '../actions'

class BadContainer extends React.Component {
  componentDidMount() {
    this.props.fetch()
  }
  render() {
    return (
      <div>
        <ComponentA
          name={this.props.nameA}
          handler1={this.props.handlerA1}
          handler2={this.props.handlerA2}
        />
        <ComponentB
          name={this.props.nameB}
          handler1={this.props.handlerB1}
          handler2={this.props.handlerB2}
        />
      </div>
    )
  }
}

const mapStateToProps = state => {
  return {
    nameA: state.a.name,
    nameB: state.b.name
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handlerA1() {
      dispatch(actions.actionA1())
    },
    handlerA2() {
      dispatch(actions.actionA2())
    },
    handlerB1() {
      dispatch(actions.actionA1())
    },
    handlerB2() {
      dispatch(actions.actionA2())
    },
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(BadContainer)
```

この例では一つのContainerで２つのコンポーネント、ComponentAとComponentBを表示しようとしています。
今回の例では２つだけですが、今後３つ、４つと表示するコンポーネントが増えていくとその分必要なハンドラーやプロパティが増えていくため、 `mapStateToProps` と `mapDispatchToProps` が肥大化してしまい見通しが悪くなります。

### 分割されている例
```jsx
// GoodContainer.jsx
import { connect } from 'react-redux'
import * as React from 'react'
import ContainerA from './ContainerA'
import ContainerB from './ContainerB'
import * as actions from '../actions'

class GoodContainer extends React.Component {
  componentDidMount() {
    this.props.fetch()
  }
  render() {
    return (
      <div>
        <ContainerA />
        <ContainerB />
      </div>
    )
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetch() {
      dispatch(actions.fetchData())
    }
  }
}

export default connect(()=> { return {} }, mapDispatchToProps)(GoodContainer)

// ContainerA.jsx

import { connect } from 'react-redux'
import * as React from 'react'
import ComponentA from '../components/ComponentA'
import * as actions from '../actions'

const mapStateToProps = state => {
  return {
    name: state.a.name,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionA1())
    },
    handler2() {
      dispatch(actions.actionA2())
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ComponentA)

// ContainerB.jsx

import { connect } from 'react-redux'
import * as React from 'react'
import ComponentB from '../components/ComponentB'
import * as actions from '../actions'

const mapStateToProps = state => {
  return {
    name: state.b.name,
  }
}

const mapDispatchToProps = dispatch => {
  return {
    handler1() {
      dispatch(actions.actionB1())
    },
    handler2() {
      dispatch(actions.actionB2())
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(ComponentB)
```

分割されている例では、もともと `componentDidMount` でデータフェッチしていた箇所だけを元のContainerに残し、 `ComponentA` と `ComponentB` に関するプロパティとハンドラーをそれぞれ `ContainerA` と `ContainerB` に分割しています。
こうすることでコードの肥大化を防ぎ見通しがかなり良くなったと思います。
ただし、全てのコンポーネントを `connected` にするとやりすぎなので適切な単位で分割する必要がありますのでご注意ください。
例えばECサイトの場合、**商品名**、**商品コメント**、**配送方法**、のようなおおざっぱな単位で分割していき、それでもコードの見通しが悪いと感じたら更に分割するというような感じで実装しています。

またContainer Componentsのテストの仕方については以前書いた記事がありますので読んで頂けると幸いです。
https://qiita.com/hiyamamoto/items/281709cc2a98268fb6c2

## Presentational Componentsの分割
前項に続きコンポーネントの分割についてです。
`Presentational Components` についての詳細は[Reduxの公式Doc](https://redux.js.org/basics/usagewithreact#implementing-presentational-components)をご確認ください。

`Presentational Components` を分割する理由としては前項と同様に見通しを良くするため、テストのしやすさのためです。
また、Reactのメリットにも書いてある再利用性を高めるという利点もあります。

まずは分割されていない例を見ていきましょう。

### 適切に分割されていない例

```bad.jsx
function Page(props) {
  return (
    <div className="container">
      <div className="header">
        <h1>{props.headerTitle}</h1>
      </div>>
      <div className="body">
        <button onClick={props.onClickForward}>進む</button>
        <button onClick={props.onClickBackward}>戻る</button>
        <p>{props.value}</p>
      </div>
      <div className="footer">
        <a href="/path/1">Link1</a>
        <a href="/path/2">Link2</a>
      </div>
    </div>
  )
}
```

こちらの例では複数の `div` 、 `button` 、 `a` 要素が出てきています。
そんなに大きなコンポーネントではありませんが既にちょっと見通しが悪くなっていると思います。

また、このコンポーネントのテストは以下になります。
※ *コンポーネントのテストは [enzyme](https://github.com/airbnb/enzyme) というライブラリの使用を前提としています*


```bad_spec.jsx
describe('<Page />', () => {
  const wrapper = shallow(<Page {...props} />)

  it('render 進むButton', () => {
    // button が複数出てくるのでうまく特定出来ない
    expect(wrapper.find('button').fisrt().contains('進む')).to.be.true
  })
})

```

このように複数の同一要素がある場合は `wrapper.find('button').first()` のように表示順を意識してコンポーネントの取得をする必要が出てきてしまい、ただ表示順が変わっただけでテストが壊れてしまいます。

では分割されている例を見てみましょう。

### 適切に分割されている例

```good.jsx
function Page(props) {
  return (
    <Container>
      <Header title={props.headerTitle} />
      <Body>
        <ForwardButton onClick={props.onClickForward} />
        <BackwardButton onClick={props.onClickBackward} />
        <BodyContent value={props.value} />
      </Body>
      <Footer>
        <Link1 />
        <Link2 />
      </Footer>
    </Container>
  )
}
```
**分割されている例**では**分割されていない例**で見られた `div` などが一切出てきていないため見通しが良くなっているのがわかると思います。
また、それぞれのコンポーネントの意味がコンポーネント名からひと目で分かるようになっています。

このコンポーネントのテストはこちらです。


```good_spec.jsx

describe('<Page />', () => {
  const wrapper = shallow(<Page {...props} />)

  it('render 進むButton', () => {
    // ForwardButton というコンポーネントがあるかテストするだけで良い
    expect(wrapper.find(ForwardButton)).to.have.length(1)
  })
})

```

**進むボタン**がコンポーネント化されたため、**戻るボタン**との表示順を気にする必要がなくなり、デザインの都合で表示順が変わったとしてもテストが壊れることがなくなりました。


## ComponentをStatelessに保つ

Reactの利点として状態をDOMから分離できるということを前述しましたが、コンポーネントからも状態を分離することで本来のコンポーネントが持つViewとしての役割だけに専念させることができます。
Reactの基本として `state` というもので状態を管理することができますが、その `state` そのものを持たないコンポーネントを `Stateless Functional Component` と呼びます。

React/Reduxアプリケーションでは `state` は基本的に `redux store` で管理し、コンポーネントでは `state` を使わないようにすることで状態管理を一元化することができます。

ただし、ボタンクリック時にモーダルを表示したり、フォーカスしたときに値を加工するだけなど、そのコンポーネント内で完結するような `state` を持つことはさほど問題にはならず、全ての状態を `redux store` で管理しなければいけないというわけではないと個人的には考えています。
あるコンポーネントの状態を別のツリーのコンポーネントで参照したい場合や、ドメインロジックに関わる状態は `redux store` で管理し、コンポーネント内部で完結する状態はそのコンポーネントの `state` として管理するように柔軟に設計するのがベターです。

### Stateless Functional Component の書き方
`state` や `lifecycleメソッド` を持たない場合は `class` シンタックスではなく、関数を使います。
コンポーネント名として関数名が使われるため、 `arrow function` より通常の関数として書くとよいです。


```jsx

function FooComponent(props)  {
  return (
    <div>
      <p>名前: {props.name}</p>
      <p>年齢: {props.age}</p>
    </div>
  )
}
```

利点

- ただの関数なのでテストしやすい
- stateを持ってないことが一発でわかる

## Reducerの分割
Reduxでは `combineReducers` という関数を使って通常Reducerを分割すると思います。
[Reduxの公式Doc](https://redux.js.org/recipes/structuringreducers/basicreducerstructure)ではReducerを分割する際、コンポーネントのレンダリングツリーで分割するのではなくドメインデータごとに分割することを推奨しています。
また、 **DomainState**、 **AppState**、 **UIState** という３つのStateに分割することが提案されています。

### DomainState
例えば、商品や取引などのドメイン特有のstateのことです。
前述したレンダリングツリーごとの分割とドメインデータごとの分割の比較をしてみます。

#### レンダリングツリーごとの分割
レンダリングツリーごとの分割は簡単に言うと画面ごとの分割ということです。
下記は `新規画面` 、 `編集画面` 、 `一覧画面` といった画面ごとに分割している例です。

```
reducers
 ├── newReducer.js
 ├── editReducer.js
 └── listReducer.js
```

#### ドメインデータごとの分割
変わってドメインデータごとの分割では、`商品` 、 `配送方法` 、 `ブランド` などのドメインデータで分割しています。

```
reducers
 ├── productReducer.js
 ├── shipphingReducer.js
 └── brandReducer.js
```

### AppState
アプリケーション全体の `state` はドメインデータ用のReducerとは別のReducerとして用意すると、見通しが良くなります。
例えば、データをローディング中かどうかを管理する `isLoading` などの `state` はこちらに含めます。

### UIState
モーダルの表示状態などのUI特有の `state` も別のReducerで管理します。
ただし、前述のようにコンポーネントの `state` として持つことも多いです。

## stateはPOJO(Plain Old JavaScript Object)
`state` は基本的に `immutable(不変) object` として扱います。
下記のようなコードはNGです。

```javascript
const defaultState = {
  foo: 'foo',
  bar: 'bar'
}
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    state.foo = action.payload  // fooだけ変更したい
    return state
  }
}
```

[immutable.js](https://facebook.github.io/immutable-js/) を使えばかんたんに不変オブジェクトを生成できます。

```javascript
import { Map } from 'immutable'
const defaultState = Map({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    return state.set('foo', action.payload)  // fooだけ変更したい
  }
}
```

上記の `Map.prototype.set` は常に新しい `Map` のインスタンスを返します。

ただし、 `Map` は普通のオブジェクトのようにプロパティにアクセスできないので、コンポーネント内でアクセスする際には `state.get('foo')` のようにする必要があります。
また、APIなどから取得してきたJSONを毎回 `Map` オブジェクトに変換する必要があるため結構面倒です。

### それ、ES2015+でできるよ

```javascript
const defaultState = {
  foo: 'foo',
  bar: 'bar'
}
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    return {
      ...state,
      foo: action.payload // fooだけ変更できる！
    }
  }
}
```

`state` は色々な場所(コンポーネント、APIクライアントなど)でアクセスするので、「immutableオブジェクトになってるか？」といちいち判定するのは面倒です。 常に `POJO` にしておくことでその手間を減らすことができます。

### Reducerからドメインロジックを分離したい場合

ドメインデータ用のReducerにドメインロジックを書いてしまうとコードがどんどん肥大化していってしまうため、下記のようにドメインデータを `models` ディレクトリ配下に書きたくなると思います。

```javascript
import MyClass from '../models/MyClass'
const defaultState = new MyClass({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    // ドメインロジックはドメインクラスに委譲
    return state.changeFoo(action.payload)
  }
}
```

このようにしたい場合は `models` 配下からクラスではなく関数をエクスポートすることで代替できます。

```reducer.js
import { changeFoo } from '../models/MyDomainModel'
const defaultState = new MyClass({
  foo: 'foo',
  bar: 'bar'
})
const reducer(state = defaultState, action) => {
  if (action.type === 'Foo Action') {
    // ドメインロジックはドメインモデルの関数に委譲
    return changeFoo(state, action.payload)
  }
}
```

```MyDomainModel.js
export const changeFoo = (model, value) => {
  // 複雑な処理
  return {
    ...model,
    foo
  }
}
```

他にも [immutable.jsのRecordを使う方法](https://design.dena.com/engineering/redux-immutablejs/)などがありますが、前述の通り変換の手間などを考えると個人的にはあまりおすすめしません。

##  FSA(Flux Standard Action)を使う

### Flux Standard Action とは
https://github.com/redux-utilities/flux-standard-action

actionの型が実装者によってまちまちになると読みづらいし不便だから標準化しましょうね、という話です。
割と界隈ではデファクトになってる気がします。

非同期処理の成功、失敗ごとに `LOAD_SUCCESS`、 `LOAD_FAILURE` のようにactionを分けるのではなく `LOAD_FINISH` のように一つにまとめることが出来ます。


```success.js
{
  type: 'LOAD_FINISH',
  payload: {
    id: 1,
    text: 'Do something.'  
  },
  meta: {
    ... // metadata
  }
}
```

```failure.js
{
  type: 'LOAD_FINISH',
  payload: new Error(),
  error: true
}
```

アクションの型が統一されて書く方も読む方も負荷が減るのでおすすめです。

## 非同期処理
reduxの登場人物(component, reducer, actionCreator)は純粋な関数の集まりなので副作用のある処理を書く場所がありません。
一般的に副作用のある処理は `Middleware層` を利用します。

非同期処理をする為のMiddlewareとしては [redux-thunk](https://github.com/reduxjs/redux-thunk) と [redux-saga](https://github.com/redux-saga/redux-saga) が有名です。

### redux-thunk vs redux-saga

#### redux-thunk
**Pros**

- APIがかんたんで学習コストがほぼ0
- サッと導入してサッと書ける

**Cons**

- actionCreatorにロジックが入り込む
- actionCreatorから純粋さがなくなる
- そのため actionCreatorのテストが複雑になる

#### redux-saga
**Pros**

- 副作用のある処理が完全に分離できる
- APIが豊富にあり並列化したりスロットルしたり色々出来る
    - [\[redux-saga\]フォームに高速で入力するとガックガクになる問題 - Qiita](https://qiita.com/hiyamamoto/items/ded562c331f60872399a)
- 高テスタビリティ

**Cons**

- 学習コストが高い
    - Generator関数はとっつきにくい
    - APIが多い

小さめのアプリケーションでは `redux-thunk`、大きくて複雑なアプリケーションでは `redux-saga`、 というように使い分けるといいでしょう。

# まとめ
どのフレームワークにも言えることですが、見通しがよくメンテしやすいアプリケーションを書くためにはコードを適切な単位で分割することが非常に重要です。
React/Reduxアプリケーションではコンポーネントを分割し再利用性を高めたり、状態を適切に分割することで、それぞれのメリットを最大限に活かせると思います。

[Reduxの公式Doc](https://redux.js.org/)では今回書いたReduxアプリケーションを設計する上での考え方が詳細に書かれていて非常に参考になるのでぜひご一読ください。