[MERN⑥] Redux Setup & Alerts

~~~~~~~~~~ (Contents) MERN ~~~~~~~~~~~
[MERN①] Express & MongoDB Setup
https://qiita.com/niyomong/private/3281af84486876f897f7
[MERN②]User API Routes & JWT Authentication
https://qiita.com/niyomong/private/c11616ff7b64925f9a2b
[MERN③] Profile API Routes
https://qiita.com/niyomong/private/8cff4e6fa0e81b92cb49
[MERN④] Post API
https://qiita.com/niyomong/private/3ce66f15375ad04b8989
[MERN⑤] Getting Started With React & The Frontend
https://qiita.com/niyomong/private/a5759e2fb89c9f222b6b
[MERN⑥] Redux Setup & Alerts
https://qiita.com/niyomong/private/074c27259924c7fd306b
[MERN⑦] React User Authentication
https://qiita.com/niyomong/private/37151784671eff3b92b6
[MERN⑧] Dashboard & Profile Management
https://qiita.com/niyomong/private/ab7e5da1b1983a226aca
[MERN⑨] Profile Display
https://qiita.com/niyomong/private/42426135e959c7844dcb
[MERN⑩] Posts & Comments
https://qiita.com/niyomong/private/19c78aea482b734c3cf5
[MERN11] デプロイ
https://qiita.com/niyomong/private/150f9000ce51548134ad
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

##1. Creating a Redux Store

boilerplate code（定型コード）

①src/store.jsファイルを生成 -> 定型コードを追加

```src/store.js
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers'; //reducersフォルダを全てimport

const initialState = {};
const middleware = [thunk];
const store = createStore(
  rootReducer,
  initialState,
  composeWithDevTools(applyMiddleware(...middleware))
);

export default store;

```
②`src/reducersフォルダ`を生成 -> src/reducers/`index.jsファイル`を生成

③`App.js`に定型コードを追加。

```src/App.js
import React, { Fragment } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Navbar from './components/layout/Navbar';
import Landing from './components/layout/Landing';
import Register from './components/auth/Register';
import Login from './components/auth/Login';
+ //Redux
+ import { Provider } from 'react-redux';
+ import store from './store';

import './App.css';

const App = () => (
+   <Provider store={store}>
    <Router>
      <Fragment>
...
      </Fragment>
    </Router>
+   </Provider>
);
```

④`reducers/index.js`に定型コードを追加。

```src/reducers/index.js
import { combineReducers } from 'redux';

export default combineReducers({});
```

⑤GoogleChromeの拡張機能(Reduxのdevtools)
・Reduxのdevtools追加`前`
![スクリーンショット 2020-09-11 12.44.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294859/c9715ef0-38df-9dc5-288a-2894ad2d2e47.png)
・devtools追加`後`
![スクリーンショット 2020-09-11 12.44.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294859/4f7ec18a-0220-8f2e-931a-322bb54181d4.png)

##2. Alert Reducer, Action & Types

Reduxの仕組みはアラート機能が一番わかりやすいのでAlert機能から進める。

①Alert Reducerファイルを生成 -> `reducers/index.js`にimport

```reducers/alert.js
import { combineReducers } from 'redux';
+ import alert from './alert';
export default combineReducers({});
```
②Alert Reducerファイル生成。

```recuers/alert.js
import { SET_ALERT, REMOVE_ALERT } from '../actions/types';

const initialState = [];

export default function (state = initialState, action) {
  const { type, payload } = action;

  switch (type) {
    case SET_ALERT:
      return [...state, payload];
    case REMOVE_ALERT:
      return state.filter((alert) => alert.id !== payload);
    default:
      return state;
  }
}
```

③Actionフォルダ生成 -> actions/types.jsファイルを生成

```actions/types.js
export const SET_ALERT = 'SET_ALERT';
export const REMOVE_ALERT = 'REMOVE_ALERT';
```

④AlertActionファイル生成

(1) uuid(Universally Unique ID)をインストール
`$ npm i uuid`
https://www.npmjs.com/package/uuid
(2) Registerコンポで記述した`アラート内容`と`AlertType(CSSから呼び出すCSSデザイン)`をtype(SET_ALERT)に乗せてdispatch->Reducer

```actions/alert.js
(1) import { v4 as uuidv4 } from 'uuid';
import { SET_ALERT, REMOVE_ALERT } from './types';

export const setAlert = (msg, alertType, timeout = 5000) => (dispatch) => {
  const id = uuidv4();
  dispatch({
    type: SET_ALERT,
(2)    payload: { msg, alertType, id },
  });

  setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout);
};
```

##3. Alert Component & Action Call

①Register.jsにエラー時のAlert発火装置を設置
(1) Connect設置:Action{setAlert}の呼び出しのみ、Stateの呼び出し(mapStateToProps)はAlertコンポーネントで呼び出す。
・export default connect(A, B)(コンポーネント)
　A: State、 B: Action
(2) PropTypesの設定: コンポーネントの props の型チェック
　`impt`(ショートカット) -> import PropTypes from 'prop-types';
　`ptfr`(ショートカット)= -> PropTypes.func.isRequired
　＊f->function, a->array, b->object
(3) Actionで記述したsetAlert(Action発火装置)を設置

```components/auth/Register.js
import React, { Fragment, useState } from 'react';
(1)+ import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
(3)+ import { setAlert } from '../../actions/alert';
(2)+ import PropTypes from 'prop-types';

(3)+ const Register = (setAlert) => {
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    password: '',
    password2: '',
  });
...
  const onSubmit = (e) => {
    e.preventDefault();
    if (password !== password2) {
(3)+      setAlert('Passwords do not match', 'danger');
    } else {
..
  return (
...
(2)+ Register.propTypes = {
+   setAlert: PropTypes.func.isRequired,
+ };

(1)+ export default connect(null, { setAlert })(Register);
```


② Alertコンポーネント
`racfp`(ショートカット) -> 'Enter'
~~~ 詳細な記述 ~~~
(1) connect設置:Stateの呼び出し(mapStateToProps)のみ、ActionはRegister.jsで発火されるので、ここではnull。
(2) PropTypesの設定: コンポーネントの props の型チェック
(3) mapStateToProps: StateからPropsにデータ(エラーメッセージ)を渡す
(4) nullではない。
(5) array(エラー項目)が0ではない(何かしらのエラーがある)
(6) JSX(HTML風に書けるJS拡張構文) -> エラーメッセージを配列する
 -> alertType: Register.jsで'danger'を設定
 -> CSSに `alert alert-danger`(赤色ハイライト等)を記述している
 -> `xxx-${ダイナミック}` <- これダイナミック(動的)っていうらしい。

```components/layout/Alert.js
import React from 'react';
(2) import PropTypes from 'prop-types';
(1) import { connect } from 'react-redux';

const Alert = ({ alerts }) =>
(4)  alerts !== null &&
(5)  alerts.length > 0 &&
(6)  alerts.map((alert) => (
    <div key={alert.id} className={`alert alert-${alert.alertType}`}>
      {alert.msg}
    </div>
  ));

(2) Alert.propTypes = {
  alerts: PropTypes.array.isRequired,
};

(3) const mapStateToProps = (state) => ({
  alerts: state.alert,
});

(1) export default connect(mapStateToProps)(Alert);
```


③Alertコンポーネントは<Switch>の上に持ってくる。

　全ての画面に`Alertコンポーネント`が常にある状態。要は、エラーが発生した時に、`Navbarコンポーネント`よりは`下`、コンポーネント(今回は`Registerコンポーネント`)より上に表示される。

```src/App.js
...
import Login from './components/auth/Login';
+ import Alert from './components/layout/Alert';
//Redux
import { Provider } from 'react-redux';
...
const App = () => (
  <Provider store={store}>
    <Router>
      <Fragment>
        <Navbar />
        <Route exact path="/" component={Landing} />
        <section className="container">
+          <Alert />
          <Switch>
...
```