Reduxアーキテクチャのサイクルをコードリーディングで一通り確認する

この文書では、[React JS Tutorialsのソースコード](https://github.com/learncodeacademy/react-js-tutorials)をコードリーディングすることで、Reduxアーキテクチャのサイクルを一通り確認します。

![fluximg.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/198108/1c7cfbf0-3fae-c214-af8c-e2731d91f200.jpeg)

## 事前知識
- reactについては公式チュートリアルは完了しているものとします。

## 準備

node.jsのインストールは完了しているものとします。

[React JS Tutorialsのソースコード](https://github.com/kabosu3d-lab/react-js-tutorials)をダウンロードしてください(※バージョン違いが生じないようにフォークしてあります)。

ダウンロードしたらプロジェクトのルートディレクトリで、

```sh
cd 5-redux-react
npm i
npm run dev
```

します。

そして、[localhost:8080](http://localhost:8080)をブラウザで開いてください。

## コードリーディング

まず`src/js/client.js`を見てください。ここがアプリケーションのエントリポイントになります。普通のReactアプリケーションと同様に、`ReactDOM.render()`を呼んでいます。

```jsx
import React from "react"
import ReactDOM from "react-dom"
import { Provider } from "react-redux"

import Layout from "./components/Layout"
import store from "./store"

const app = document.getElementById('app')

ReactDOM.render(<Provider store={store}>
  <Layout />
</Provider>, app);
```

`Layout`コンポーネントを確認します。`src/js/components/Layout.js`を見てください。

```jsx
import React from "react"
import { connect } from "react-redux"

import { fetchUser } from "../actions/userActions"
import { fetchTweets } from "../actions/tweetsActions"

@connect((store) => {
  return {
    user: store.user.user,
    userFetched: store.user.fetched,
    tweets: store.tweets.tweets,
  };
})
export default class Layout extends React.Component {
  componentWillMount() {
    this.props.dispatch(fetchUser())
  }

  fetchTweets() {
    this.props.dispatch(fetchTweets())
  }

  render() {
    const { user, tweets } = this.props;

    if (!tweets.length) {
      return <button onClick={this.fetchTweets.bind(this)}>load tweets</button>
    }

    const mappedTweets = tweets.map(tweet => <li key={tweet.id}>{tweet.text}</li>)

    return <div>
      <h1>{user.name}</h1>
      <ul>{mappedTweets}</ul>
    </div>
  }
}

```

画面が表示される時(componentWillMount)にユーザーを取得し、画面に表示しています。この動作に沿って説明をしていきます。

`src/js/components/Layout.js`の15行目を見てください。

```jsx
  componentWillMount() {
    this.props.dispatch(fetchUser())
  }
```

propsのdispatchを呼んでいますね。

ここでpropsにdispatchがあると言うのは、このコンポーネントへ`connect`しているからです。`connect`は、7行目にあります。

```jsx
@connect((store) => {
  return {
    user: store.user.user,
    userFetched: store.user.fetched,
    tweets: store.tweets.tweets,
  };
})
export default class Layout extends React.Component {
```

`@`はDecoratorといって、コード上「次」にある要素に対して、影響を及ぼすことができる機能です。今回のコード例では、これは「次」にある`Layout`コンポーネントに対する`connect`の記述です。`connect`することで、Reactコンポーネントにthis.props.dispatchが生えて、dispatchできるようになります。

src/js/components/Layout.jsの15行目に戻りましょう。

```jsx
componentWillMount() {
  this.props.dispatch(fetchUser())
}
```

ここでは`fetchUser()`というActionをdispatchしています。

このdispatchの結果、ユーザーの情報が読み込まれて画面が更新されることになるわけです。ここではその流れを追っていきます。

`src/js/actions/userActions.js`を見てください。

1行目に`fetchUser()`があります。ここは`FETCH_USER_FULFILLED`というタイプのアクションを作成しています。またpayloadにユーザーのデータが入っています。これがdispatchされているわけです。

dipatchされると、それをreducerが処理し、storeを変更します。

`src/js/reducers/userReducer.js`を見てください。

19行目がFETCH_USER_FULFILLEDを処理するreducerです。

```js
      case "FETCH_USER_FULFILLED": {
        return {
          ...state,
          fetching: false,
          fetched: true,
          user: action.payload,
        }
      }
```

reducerが何をやっているかと言うと、`state`を変更します。

`...state,`で現在のstateを展開し、`fetching`,`fetched`,`user`を変更して、returnします。returnされた結果が新しいstateになります。

ここでいう`state`というのはアプリケーション全体におけるただ一つの`state`です。アプリケーション内の`state`を全てまとめて一元管理するのがreduxの考え方です。

reactの感覚でいうと`state`は個々のコンポーネントにあるもののように思われますが、それとは違うので考え方の切り替えが必要です。

さて、こうして変更された`state`は画面に反映されなければなりませんが、それはどのようにされるのでしょうか。再度、`Layout`コンポーネントを確認します。`src/js/components/Layout.js`の7行目を見てください。

```jsx
@connect((store) => {
  return {
    user: store.user.user,
    userFetched: store.user.fetched,
    tweets: store.tweets.tweets,
  };
})
export default class Layout extends React.Component {
```

`connect`が、reduxの`state`と画面の`props`の紐付けを行います。returnされた要素が画面の`props`として渡されます。

`state`が更新されると、reduxは以前のstateと比較を行い、更新されている部分のデータを使っている画面コンポーネントについて再描画を行います。

ここまででアクションの発行から画面の更新までの流れを一通り確認できました。