React- Redux 入門

Reduxの学習メモです

#Fluxフローについて
まずはReduxにおけるFluxフローの全体像を理解してみます

##なぜReduxを使うのか
1. stateの見通しをよくするため
2. どこからでもstateを参照/変更可能にするため
3. モジュールを疎結合にするため<br>
=機能Aと機能Bがお互いに影響しあわない

##ReactとReduxのstate管理の違い
###Reactのみでstateを管理する場合
複数コンポーネントで利用しているstateに変更が発生した場合、
コンポーネントの親子関係を辿って変更を共有しないといけない。(stateのバケツリレー)
→親子関係が深く複雑になるほど管理が大変になる。

###Reduxを使ってstateを管理する場合
複数コンポーネントで利用するstateをStoreに持たせることで、Storeに直接、変更をリクエストしたり、バケツリレーなしで関係するコンポーネントに変更を反映することができる。


##Fluxフローとは
1. データフロー設計の1つ
2. データが常に1方向に流れる
3. イベントによってデータが変化（イベント駆動）

Flux思想をReactの状態管理に適用したライブラリ
= **Redux**

##Fluxフロー図
![flux_flow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/440593/3119d80b-81cd-c6d8-cb31-a28878ba1781.png)


#Actionsを書いてstateの変更を依頼する

##Actionsの役割
アプリからStoreへデータを送るためのpayload(データの塊)を渡す役割
→アプリから受け取ったデータをReducersへ渡す

##なぜActionsを使うのか
純粋にデータを渡す処理だけを記述するため
どのstateをどのように変更するのかについてはReducersに任せる

##Actionsの書き方
ユーザーがサインインした時にStoreで管理しているユーザー情報の変更依頼を投げるAction

```javascript
// Action typeを定義してexport
export const SIGN_IN = "SIGN_IN";

// Actionsはプレーンなobjectを返す
export const signInAction = (userState) => {
  // typeとpayloadを記述する
  return {
    type: "SIGN_IN",
    payload: {
      isSignedIn: true,
      uid: userState.uid,
      username: userState.username
    }
  }
};
```

#Reducersの作り方とスプレッド構文の使い方
Reducersはstateの変更を管理する役割を持っている

##Reducersの役割
Actionsからデータを受け取り
Storeのstateをどう変更するのかを決める
→Store内のstate(状態)の管理人

##initialStateを作る

```javascript
const initialState = {
  users: {
    isSignedIn: false,
    uid: "",
    username: ""
  }
};

export default initialState
```

- Storeの初期状態(アプリケーション起動時)
- アプリに必要なstateを全て記述
- exportしておく

##Reducersを作る

```javascript
import * as Actionss from './actions';
import initialState from "../store/initialState";
```

1. actionsファイル内のモジュールを全てimport(Actionsという名前をつける)
2. initialStateをimport

```javascript
export const UserReducer= (state = initialState.users, action) => {
    switch (action.type) {
        case Actions.SIGN_IN:
            return {
                ...state,
                ...action.payload
            };
        default:
            return state
    }
};
```

###スプレッド構文について
スプレッド構文は配列やオブジェクトの要素を展開する
spread:広げる、開く、塗る

```javascript
const payload = {
  uid: "1",
  username: "hoge"
}

console.log({...payload});
// {uid: "1", username: "hoge"}

// Merge Objects
const state = {
  isSignedIn: false,
  uid: "0",
  username: "fuga"
}
console.log({...state, ...payload});
// {isSignedIn: false, uid: "1", username: "hoge"}
```

#Redux(Store)とReactを接続してstateを変更する
Storeはstateを保存する

1. StoreとReducersを関連づける
2. Redux(Store)とReactを接続する
3. Storeの状態を変更する

Store | モジュールのimport


```src/reducks/store/store.js
// reduxモジュールのimport
import {
    createStore as reduxCreateStore,
    combineReducers
} from 'redux';

// Reducersのimport
import { ProductsReducer } from '../products/reducers';
import { UsersReducer } from "../users/reducers";

export default function createStore() {
    // reduxのcreateStoreメソッドをreturn
    return reduxCreateStore(
        // combineReducersでstateを生成
        combineReducers({
            products: ProductsReducer,
            users: UsersReducer
        })
    );
}
```

##combineReducers()とは？
1. 分割したReducersをまとめる
2. stateのカテゴリ毎
3. オブジェクトをreturnする(stateのデータ構造)

```javascript
combineReducers({
  products: ProductsReducer,
  users: UsersReducer
})
```
↓

```javascript
{
  products: {
    // productsのstate
  },
  users: {
    isSignedIn: false,
    uid: "",
    username: ""
  }
}
```

##StoreとReactアプリの接続

```src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import createStore from "./reducks/store/store";
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

export const store = createStore();

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

serviceWorker.unregister();
```

###react-reduxのProviderとは
1. propsにstoreを渡す<br>
→ラップしたコンポーネントにstoreの情報を渡す
2. Reactコンポーネント内でreact-reduxのconnect関数を使えるようにする<br>
→ReactとReduxを接続してstoreを変更できるように

#ルーティングの設定
connected-react-routerを利用してルーティングする

##ルーティング用ライブラリ
1. react-router v4以降<br>
Reactのルーティング用ライブラリ<br>
Reduxとは関係なく、Reactで利用可能
2. connected-react-router<br>
ReduxのStoreでルーティングを管理<br>
react-router v4 & v5と互換性あり

##middlewareの導入

```src/reducks/store/store.js
import {
    createStore as reduxCreateStore,
    combineReducers,
    applyMiddleware
} from "redux";
import { connectRouter, routerMiddleware } from "connected-react-router";
import { UsersReducer } from "../users/reducers";

export default function createStore(history) {
    return reduxCreateStore(
        combineReducers({
            router: connectRouter(history),
            users: UsersReducer
        }),
        applyMiddleware(
            routerMiddleware(history)
        )
    );
}
```
##StoreとRouterの接続
```src/index.js
import { ConnectedRouter } from "connected-react-router";
import * as History from 'history';

const history = History.createBrowserHistory();
export const store = createStore(history);

ReactDOM.render(
  <Provider store={store}>
      <ConnectedRouter history={history}>
          <App />
      </ConnectedRouter>
  </Provider>,
  document.getElementById('root')
);
serviceWorker.unregister();
```
##Routerコンポーネントを作る
```src/Router.jsx
import React from "react";
import { Route, Switch } from "react-router";
import { Login, Home } from "./templates";


const Router = () => {
    return (
        <Switch>
            <Route exact path="/login" component={Login} />
            <Route exact path="(/)?" component={Home} />
        </Switch>
    );
};

export default Router;
```

1. Routeコンポーネントでパスとコンポーネントを指定する
2. exactは「パスとぴったり一致したら」という意味。<br>
Switchコンポーネントと一緒に使う。

`(/)?` スラがあってもなくてもという意味

##Routerコンポーネントを使う

```src/App.jsx
import React from "react";
import Router from "./Router";

const App = () => {
    return (
        <main>
            <Router />
        </main>
    );
};

export default App;
```

##templatesファイルの作成
```src/templates/Login.jsx
import React from "react";
import { useDispatch } from "react-redux";
import { push } from "connected-react-router";

const Login = () => {
    const dispatch = useDispatch();
    return (
        <div>
            <h2>ログイン</h2>
            <button onClick={() => dispatch(push('/'))}>
                ログイン
            </button>
        </div>
    );
};

export default Login;
```

```src/templates/Home.jsx
import React from "react";

const Home = () => {
    return (
        <h2>Home</h2>
    );
};

export default Home;
```

#re-ducksパターンでファイルを管理する
ディレクトリ構成のベストプラクティス

##re-ducksパターンとは
1. Reduxのディレクトリ構成
2. ファイルを管理しやすくする
3. Ducksパターンから派生

####Ducksパターン以前
```
actions
├ products.js
└ users.js

reducers
├ products.js
└ users.js
```

問題点：<br>
ファイル名が同じなので、<br>
actionについて書いてるんだっけ？reducerだっけ？となる

####Ducksパターン
```
modules
├ products.js
└ users.js
```

reducerはactionを参照するんだから一つにまとめちゃえとなったのが
Ducksパターン

#####re-ducksパターン
```
users
├ ctions.js
├ index.js
├ operations.js
├ reducers.js
├ selectors.js
└ types.js
```

###なぜre-ducksパターン？
Ducksパターンの課題を解決するため
(ファイルの肥大化)

```modules/users.js
export const UsersReducer= (state = initialState.users, action) => {
    ...
};

export const SIGN_IN = "SIGN_IN";
export const signInAction = (userState) => {
    ...
};

export const SIGN_OUT = "SIGN_OUT";
export const signOutAction = () => {
    ...
};
```

###re-ducksパターンのメリット
1. actionsとreducersがシンプルになる
2. ファイルが肥大化しにくくなる
3. ファイル毎の役割が明確で管理しやすい

```
products
├ actions.js
├ index.js
├ operations.js
├ reducers.js
├ selectors.js
└ types.js

users
├ actions.js
├ index.js
├ operations.js
├ reducers.js
├ selectors.js
└ types.js
```

###各ファイルの役割
####operations
1. 複雑な処理を任せられる
2. redux-thunkで非同期処理を制御する
3. Actionsを呼び出す

####types
1. TypeScriptで使う
2. 型定義を記述してexport

####selectors
1. Storeで管理しているstateを参照する関数
2. reselectというnpmモジュールを使う

```src/reducks/users/selectors.js
import { createSelector } from "reselect";

const usersSelector = (state) => state.users;

export const getUserId = createSelector(
  [usersSelector],
  state => state.uid
);
```

#redux-thunk
Redux内の非同期処理を制御する

##redux-thunkとは
Reduxで非同期処理を制御するライブラリ<br>

通常のActionsはaction objectを受け取る<br>
= 関数を受け取ることができない<br>
= async/awaitやPromiseを使えない<br>

##redux-thunkの導入方法
```src/reducks/store/store.js
import thunk from "redux-thunk";

export default function createStore(history) {
    return reduxCreateStore(
        combineReducers({
            router: connectRouter(history),
            users: UsersReducer
        }),
        applyMiddleware(
            routerMiddleware(history),
            thunk
        )
    );
}
```
1. モジュールimport
2. applayMiddleware()に追加

###redux-thunkの基礎文法

```src/reducks/users/operations.js
export const signIn = (email, password) => {
    return async (dispatch, getState) => {
        const state = getState();
        const isSinedIn = state.users.isSignedIn;

        if (!isSinedIn) {
            const userData = await emailSignIn(email, password);
            dispatch(signInAction({
                isSinedIn: true,
                uid: '00001',
                username: 'hoge'
            }))
        }
    };
};
```


#コンテナの役割
Storeと接続されたコンテナコンポーネントを作る

コンテナコンポーネントはStoreとコンポーネントの中継役

##コンテナコンポーネントの役割
Reduxの世界とReactの世界を繋ぐ

1. stateをフィルタリングしてコンポーネントに渡す
2. Storeからdispatchする関数=Actionsをコンポーネントに渡す

##いつ使う？
コンテナーコンポーネントを使用するのに必要な手続きが面倒
→現在はRedux Hooksを使う方法がオススメ

明示的にstateをフィルタリングしたいとき

##connect()の使い方
```src/reducks/containers/Login.js
import LoginClass from '../templates/LoginClass';
import {compose} from 'redux';
import {connect} from 'react-redux';
import * as Actions from '../reducks/users/operations';

const mapStateToProps = state => {
  return {
    users: state.users // 渡したいstateだけをオブジェクト型で記述
  }
}
```

```src/reducks/containers/Login.js
const mapDispatchToProps = dispatch => {
  return {
    actions: {
      signIn() {
        dispatch(Actions.signIn()) // StoreからDispatchする関数
      }
    }
  }
}

export default compose(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )
)(LoginClass);
```

```src/reducks/templates/LoginClass.jsx
import React, {Component} from 'react';

export default class LoginClass extends Component {
  render() {
    return (
      <div>
        <h2>ログイン</h2>
        <button onClick={() => this.props.actions.signIn()}>
          ログインする
        </button>
      </div>
    )
  }
}
```

##Hooksでの代替方法
mapStateToProps → useSelector()
mapDispatchToProps → useDispatch()