ReduxとReact-Reduxの超入門チュートリアル

やっと**Redux**が理解できたので、忘れないうちにチュートリアル形式でまとめておきます。

# 学習の前に
- Node.jsが必要です
    - https://nodejs.org/ja/
    - `anyenv`からの`nodenv`が便利でオススメです
        - https://github.com/anyenv/anyenv
- この記事は、以下の環境で実行しています
    - macOS バージョン10.14.6
    - Node.js v10.16.0

# まず理解すること

- **Redux**と**React-Redux**は違うもの
    - **Redux**は、設計方法
    - **React-Redux**は、ReduxとReactをつなぐもの

これを頭に入れておくと、今後の理解がスムーズになるかと思います。

では早速、Reduxを使ってみましょう。

# Redux
いったん、Reactのことは忘れて**Redux**のみに触れていきましょう。

## まずはシンプルに使ってみる

適当なディレクトリを作成し、以下を実行してください。

```console
$ npm init -y
$ npm i redux
```
これで、**Redux**が使えるようになりました。
続いて、`index.js`を作成し、以下を記述します。

```js
// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch({ type: 'INCREMENT' });
```

`Node.js`で`index.js`を実行します。

```console
$ node index.js
// -> 1
```

ターミナルに`1`が表示されれば、成功です。

これで、あなたもReduxが使えるようになりました！
おめでとうございます (^^)

では、**Reducer**／**Store**／**Subscribe**／**Dispatch**部分を、それぞれ解説します。

### Reducer

**Reducer**は、状態管理したい値（state）と、その値を変化させるための処理が書かれている関数です。
この時点では、まだ**Redux**とはなんの関係もありません。ただの関数です。

```js
function reducer(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    default:
      return state;
  }
};
```

第1引数に`state`、第2引数に`action`を入れ、**switch 文**を使い、`action`の値によって、`state`の値を変えて`return`します。

`state`には、状態管理したい値の初期値を代入しておきます。今回の例では、数値の`0`を代入しています。

`action`は、後に出てくる`store.dispatch({ type: 'INCREMENT'})`メソッドの引数`{ type: 'INCREMENT' }`が渡されてきます。

### Store

**Store**は、`state`を管理するためのオブジェクトです。
`createStore()`の引数に**Reducer**を入れる事によって作成できます。

詳しくは後述しますが、`store.getState()`で`state`の現在の状態を取得したり、`store.dispatch()`で状態を変化させたりすることができます。

```js
const store = require('redux').createStore(reducer);
```

`npm i redux`でインストールした`redux`を`require`し、`createStore()`メソッドを呼び出しています。
そして、`createStore()`の引数に、上で解説した**Reducer**の関数を入れ、`store`定数に代入しています。

`store`はオブジェクトになっていて、`state`の取得や、その値を変化させることができる`store.dispatch()`を使用することができます。

### Subscribe

```js
store.subscribe(() => console.log(store.getState()));
```

`store.subscribe()`メソッドは、`store.dispatch()`が呼び出されたら、引数のコールバック関数を実行します。

そして、`store.getState()`メソッドは、現在の`state`を取得します。

`store.subscribe()`の引数のコールバック関数で、`console.log(store.getState())`を実行しているため、`store.dispatch()`するたびに、現在の`state`をコンソールに表示します。

### Dispatch

```js
store.dispatch({ type: 'INCREMENT' });
```

`store.dispatch()`の引数に入れた値が、Reducerの`action`に渡されます。

よって、上の例では、`{ type: 'INCREMENT' }`が`reducer`関数の`action`に渡されるので、`switch`の`action.type`は`'INCREMENT'`の文字列になり、`case 'INCREMENT'`が実行され、`state + 1`が`return`されて、`state`の値が0から1に変化します。

以下のコメントアウト付きReducerを見ながら、動きを確認してみましょう。

```js
/**
 * state に状態管理したい値を代入する。
 * store.dispatch({ type: 'INCREMENT' })が実行された場合、
 * action には { type: 'INCREMENT' } が入っている。
*/
function reducer(state = 0, action) {
  /** 
   * store.dispatch({ type: 'INCREMENT' })が実行された場合、
   * action.type は 'INCREMENT'
  */
  switch (action.type) {
    case 'INCREMENT':
      return state + 1; // こちらが実行され、もともとの state = 0 に +1 されて 1 が return される
    default:
      return state;
  }
}
```

`return`された`state`は、`store`で保持され、`store.getState()`で取得することができます。

## Actions と Action Creators

**Rreducer**の`case`で指定した文字列は、通常**Action**として切り分けます。
また、`store.dispatch()`の引数の`{ type: 'INCREMENT' }`も、**Action Creators**として、関数にします。

```js
// Actions
const INCREMENT = 'INCREMENT';

// Action Creators
function increment() {
  return {
    type: INCREMENT
  }
}

// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT: // Action の INCREMENT に置き換え
      return state + 1;
    default:
      return state;
  }
}

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch(increment()); // Action の INCREMENT に置き換え
```

**Actions**として切り分けたことにより、記述量は増えてしまいますが、ファイルを分けて`import`や`export`するときに、使い勝手が良くなります。

**Action Creators**は、関数の引数のオブジェクトに`type`以外を追加することができるので、**Reducer**での処理をコントロールしやすくなります。

たとえば、以下のように`increment()`関数の引数の数値によって、カウントアップする数値を柔軟に変更させることができます。

```js
// Actions
const INCREMENT = 'INCREMENT';

// Action Creators
function increment(count) { // 引数を追加
  return {
    type: INCREMENT,
    count // 引数を return するオブジェクトに追加する
  }
};

// Reducer
function reducer(state = 0, action) {
  switch (action.type) {
    case INCREMENT:
      return state + action.count; // action に count が追加される
    default:
      return state;
  }
};

// Store
const store = require('redux').createStore(reducer);

// Subscribe
store.subscribe(() => console.log(store.getState()));

// Dispatch
store.dispatch(increment(2)); // ここで state は 2 になる
store.dispatch(increment(3)); // さらにここで state は 5 になる
```

ここまで理解できれば、**Redux**の[ベーシックチュートリアル](https://redux.js.org/basics/basic-tutorial)の**Data Flow**までは理解できるかと思います。
（英語はがんばってくださいｗ）

# React-Redux

さて、ここまでは**Redux**について学んできました。
ここからは、いよいよ**Redux**と**React**をつなげる**React-Redux**の出番です！

とりあえず、`create-react-app`でReactのプロジェクトを作りましょう。

適当なディレクトリで、以下を実行してください。

```console
$ npx create-react-app redux-example
$ cd redux-example && npm start
```

`localhost:3000`でReactのプロジェクトが立ち上がればOKです。

## Redux と React-Redux のインストール

ReduxとReact-Reduxをインストールします。

```console
$ npm i redux react-redux
```

`src/reducers.js`と`src/actions.js`を作成し、**Redux**を学んだときに作成した、`index.js`の**Actions**、**Action Creator**と**Reducer**部分をそれぞれコピペして、少し修正します。

**src/actions.js**

```js
// Actions
export const INCREMENT = 'INCREMENT'; // exportして外部で使用できるように修正

// Action Creators
export function increment(count) { // exportして外部で使用できるように修正
  return {
    type: INCREMENT,
    count
  }
};
```

**src/reducers.js**

```js
import { INCREMENT } from './actions'; // Actionsをimport

// Reducer
export default function reducer(state = 0, action) { // export defaultして外部で使用できるように修正
  switch (action.type) {
    case INCREMENT:
      return state + action.count;
    default:
      return state;
  }
};
```

**Store**は`src/index.js`に書いてみましょう。ここで**React-Redux**を使い、ReactとReduxをつなぎます。

以下のように、`src/index.js`を書き換えてみてください。

**src/index.js**

```jsx
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux'; // createStoreをimport
import { Provider } from 'react-redux'; // Providerコンポーネントをimport
import reducer from './reducers'; // reducer関数をimport
import App from './App';

const store = createStore(reducer); // storeオブジェクトを作成

ReactDOM.render(
  // <App />コンポーネントを<Provider store={store}></Provider>でラップする
  <Provider store={store}>
    <App />
  </Provider>
, document.getElementById('root'));
```

`<Provider store={store}></Provider>`でラップされたコンポーネント内であれば、**Store**にアクセスすることができます。

これで、React上でReduxを使う準備が整いました。

続いて、**Redux Hooks**を使い、`state`の取得と変更をしてみましょう！

## Redux Hooks

**Redux Hooks**の登場により、簡単に**Redux**の**Store**にアクセスできるようになりました。`src/App.js`で`useSelector`と`useDispatch`を使用し、stateの取得と変更をしてみましょう。

`src/App.js`を以下のように書き換えてみてください。

**src/App.js**

```jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux'; // これがRedux Hooks
import { increment } from './actions'; // Action Creatorsのincrement関数をimport

function App() {
  const count = useSelector(state => state); // useSelector で state を取得
  const dispatch = useDispatch(); // useDispatch で state を更新

  return (
    <div className="App">
      {/* 現在のstateを表示 */}
      <div>{count}</div>
      {/* クリックするたびに、stateを更新 */}
      <button onClick={() => dispatch(increment(1))}>INCREMENT</button>
    </div>
  );
};

export default App;
```

`localhost:3000`に`state`の値が表示されているかとい思います。また、その下の「INCREMENT」ボタンをクリックすると、数値が書き換わるはずです。

`useSelector`を使うと、`store`から`state`を取得することができます。後述する、`useDispatch`による`dispatch()`が実行されると、`state`の比較が行われ、変更があれば再レンダリングされます。

**Redux**のときに出てきた、`store.subscribe(() => console.log(store.getState()));`をやっている感じですね。

`useDispatch`を使うと、`store.dispatch()`にアクセスすることができます。

こちらも、**Redux**のときに出てきた、`store.dispatch(increment());`ですね。

# おわりに
かなりシンプルでしたが、**Redux**と**Redux-React**のチュートリアルでした。

ここまで理解できれば、**[Redux](https://redux.js.org/)**と**[React-Redux](https://react-redux.js.org/)**のドキュメントはだいぶ理解できるようになるかと思います。まだまだ解説していない部分もありますので、そこは一緒に学習していきましょう。

少しでも、皆様の学習の手助けになれば、これ幸いです。

それでは、また。