日本一わかりやすいReact-Redux入門#4~#7 学習備忘録

## はじめに
この記事は、Youtubeチャンネル『トラハックのエンジニア学習ゼミ【とらゼミ】』の『[日本一わかりやすいReact-Redux入門](https://www.youtube.com/watch?v=FBMA34gUsgw&t=109s)』の学習備忘録です。

前回講座で学んだ React に続き、大規模アプリ開発の必須のライブラリである Redux についても勉強をしていきます。

前回の記事は[こちら](https://qiita.com/ddpmntcpbr/items/b81bbdac0975b7450321)。


## #4...Actionsを書いてstateの変更を依頼しよう

- Actionsは、Fluxフローにおいて窓口の役割を果たす。
- アプリから受け取った「stateの変更依頼」をReducersに渡す
- Actionsはプレーンなオブジェクトを返す


```react:src/Reducks/users/action.js
export const SIGN_IN = "SIGN_IN";
export const signInAction = {userState} => {
  return {
    type: "SIGN_IN",
    payload: {
      isSignedIn: true,
      uid: userState.uid,
      username: userState,username
    }
  }
};

export const SIGN_OUT = "SIGN_OUT";
export const signOutAction = () => {
  return {
    type: "SIGN_OUT",
    payload: {
      isSignedIn: false,
      uid: "",
      username: ""
    }
  }
}
```

## 5...Reducersの作り方とスプレッド構文の使い方

- ReducersはActionsからデータを受け取り、Storeのstateをどう変更するか決める。
- Reducersは、Storeの”現在の状態”と”初期の状態”の情報を保有する。
- 初期状態の定義は、``initialState.js``で行う。


```react:src/reducks/store/initialState.js
const initialState = {
  users: {
    isSignedIn: false,
    uid: "",
    username: ""
  }
};

export default initialState
```

```react:src/users/reducers.js
import * as Actions from './actions'
import initialState from '../store/initialState'

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

``...``はスプレッド構文というjavascriptの文法で、オブジェクトの展開を表す。すなわち、return文は下記と同じ意味になる・

```react:
      return {
        isSignedIn: false,
        uid: "",
        username: ""
        isSignedIn: true,
        uid: userState.uid,
        username: userState,username
      }
```

同じkeyのものが繰り返された時は、後半にあるものが上書きして残る。

## #6...Redux(Store)とReactを接続してstateを変更しよう

- Storeはstateを保存する役割。Reducersにより、Store内部のstateの値を変更される。

```react:src/reducks/store/store.js
import {
  createStore as reduxCreateStore,
  combineReducers,
} from 'redux';

import {UsersReducer} from '../users/reducers';

export default function createStore() {
  return reduxCreateStore(
    combineReducers({
      users: UsersReducer,
    })
  );
}

```
- ``combineReducers()``は、複数のReducerを束ねる関数。今は``UsersReducer``しかないが、Reducerが増えた場合、ここの記述が複数行になる。

storeをReactアプリと接続するため、``index.js``を編集する。

```javascript: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')
);

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

- ``export const store = createStore();``で、Storeを生成。
- ``<Provider>``タグで``<App />``をラッピングすることで、StoreをReactアプリに渡す。

本当にstoreとReactアプリが接続されたのかを、``Redux Hooks``を用いて確認する。

```javascript:App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {useDispatch. useSelector} from "react-redux";

function App() {
  const dispatch = useDispatch()
  const selector = useSelector((state) => state)

  console.log(selector.users)

  return (
...
```

[localhost:3000](http://localhost:3000/)をGoogle Chromeで開き、検証ツールから``console.log()``の結果を見に行く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/c9220cab-44a6-237a-f0b7-4c0144c8878e.png)

``initialState``として定義しているuserオブジェクトの初期値が、Reactアプリに渡っているのが分かります！

次に、このstateの値を更新してみます。``src/reducks/users/actions.js``で定義した``signInAction()``が発火するボタンを``App.js``内に追記します。

```javascript:src/App.js
import React from 'react';
import logo from './logo.svg';
import './App.css';
import {useDispatch, useSelector} from "react-redux";
import {signInAction} from "./reducks/users/actions"

function App() {
  const dispatch = useDispatch()
  const selector = useSelector((state) => state)

  console.log(selector.users)

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        <button onClick={() => dispatch(signInAction({uid: "0001", username: "torahack"})) } >
          Sign In
        </button>
      </header>
    </div>
  );
}

export default App;
```

``<button>``タグの``onClick``イベントに、先ほどの``signInAction()``を渡しています。このボタンをクリックすると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/cfb2583d-020c-274b-2a54-dd39f0748381.png)

stateの値が更新されているのが分かります！

## #7...URLに応じたコンポーネントを表示しよう
**connected-react-router**というReduxライブラリを使用する。これは、Reduxのstoreを利用してルーティングを管理するというもの。

``store.js``と``index.js``を編集する。

```react:src/reducks/store/store/js
import {
  createStore as reduxCreateStore,
  combineReducers,
  applyMiddleware
} from 'redux';
import {connentReactRouter, routerMiddleware} from "connected-react-router";

import {UsersReducer} from '../users/reducers';

export default function createStore(history) {
  return reduxCreateStore(
    combineReducers({
      router: connentReactRouter(histroy),
      users: UsersReducer,
    }),
    applyMiddleware(
      routerMiddleware(history)
    )
  );
}
```

- ``createStore()``が新たに``history``という引数を受け取っている。これは、サイト遷移の履歴が入っている
- ``router``というstate名で、この``history``を管理する。

```javascript: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';
import * as History from 'history';

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

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

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

- ``<ConnectedRouter>``でさらに``<App />``をラッピング。

具体的なルーティングの定義を実装する、実装は、``Router.jsx``という特殊なコンポーネントファイルを作成して行う。

```react: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
```

- ``<Route>``タグ内で、パスとコンポーネントの対応を記述する。
- ``exact path=``が完全一致、``path=``が部分一致。静的なルーティングに対しては前者を使い、動的なルーティングに対しては後者を使う("/posts/:id"のようなケース)

この時点では、templatesファイルは未作成。これを作る前に、``App.js``を変更する。拡張子を``App.jsx``にした上で、中身をがらっと変える。

```react:src/App.jsx
import React from 'react'
import Router from './Router'

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

export default App;
```

``<main>``タグでRouterコンポーネントを呼び出している。この``<Router />``の中身が、パスごとに切り替わるイメージ

続いて、templatesファイルを作る。templatesファイルは、静的ページにつき１個ずつ存在し、そのページ(ルーティング)において親コンポーネントの役割を果たす。

作成するファイルは以下の通り。
- src/templates/Login.jsx
- src/templates/Home.jsx
- src/templates/index.js

``index.js``はエントリーポイントの役割を果たす。

```react: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
```
- ``useDispatch()``で store に紐付いた dispatch が取得できる。
- ``push()``を実行すると、引数のパスにページを遷移させることができる。現時点では、ダミーとしてルートパスを指定。

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

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

export default Home
```

```javascript:src/templates/index.js
export {default as Login} from './Login'
export {default as Home} from './Home'
```

ブラウザで確認すると、

| path = "/" | path = "/login" | 
| --- | --- | 
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/9086608f-f516-0b4b-8ab9-b39b2c6db63c.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540216/1aac5edf-149b-982f-74cf-17c573ef0efd.png)|

上手く表示されています！また、ログインボタンを押すことで、Home画面へ遷移できるようになっています。

## おわり

今回はここまで。

第三回講座で概念を学んだFluxフローについて、実際に手を動かすことでなんとなーく分かってきた気がしてきました。

開発フレームワークにおける「どこに何を書くべきかが決まっている」という状態は、初期の学習コストは高くなりがちな反面、慣れることさえできれば、開発だけでなく保守・改修も効率的に行うことができる、というメリットがあると思います。Reduxもその類かと思われるので、頑張って習得していきたいです。

次回記事は今後更新予定。