坂本研のゼミ室

React Reduxを使ってみる

Reduxとは

Reduxとは状態管理をするライブラリ
Reactでは様々なコンポーネントの状態が個別で扱われるが、それらを統合して管理する機能を提供している

特徴

  • すべてのデータはアプリケーションごとに1つだけ用意されるStoreに保管される
  • Storeの値は読み取り専用で、直接書き換えは許可されていない
  • Storeの値を変更するにはReducerを使う

インストール

Reduxのインストール

npm install --save redux

React Reduxのインストール

npm install --save react-redux

Reduxで用意するもの

Store

データを保管し管理する
ここに保管される値はstateと呼ばれる
Storeの中にstateとReducerが組み込まれているイメージ?
f:id:TakaShinoda:20190919003359p:plain

Provider

Storeを他のコンポーネントに受け渡す

Reducer

Storeに保管されるstateを変更する

実際に使っていく

今回はボタンを押すと数字が1ずつ増えていくものを作っていく
f:id:TakaShinoda:20190919013709p:plain

create-react-appコマンドで雛形を作る

create-react-app ディレクトリ名

create-react-appコマンドをインストールしてない場合は下記のコマンドでインストールする

npm install -g create-react-app

サーバーを起動する

npm start

または

yarn start

ディレクトリ構造

ディレクトリ構造は下記のように編集しました
srcの中にcomponentsを作成し、App.js, Button.js, Message.js, Store.jsがあるだけです

│
├── src
│   ├── .DS_Store
│   ├── App.css
│   ├── App.test.js
│   ├── components
│   │   ├── App.js
│   │   ├── Button.js
│   │   ├── Message.js
│   │   └── Store.js
│   ├── index.css
│   ├── index.js
│   └── serviceWorker.js

index.js

index.jsを下記のように下記のように書き換える

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import App from './components/App';
import countStore from './components/Store'
import * as serviceWorker from './serviceWorker';

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

serviceWorker.unregister();

Providerタグの中にコンポーネントを記述するとことで、
内部のコンポーネントでストアに保管されている値や処理が使えるようになる

Store.js

ここにストアを記述していく

import { createStore } from 'redux';

//ステート
const state_value = {
    count: 0,
};

// レデューサー
function countReducer(state = state_value, action) {
    switch (action.type) {
        case 'INCREMENT':
        return {
            count:state.count + 1,
        };
        default:
        return state;
    }
}

//createStoreでストアを作成しエクスポート
export default createStore(countReducer);
stateの用意

ストアに保管するステートの値を用意する
今回は数字をカウントするcountを用意

//ステートの用意
const state_value = {
    count: 0,
};
Reducerの作成

function 関数名(state = 先ほど作成したstate名, action)
第一引数のstateには、ストアに保管するstateを指定する
第二引数のactionはReducerを呼び出した時の情報が保管され、
さらにtypeというプロパティが必ず用意される
次にswitchによる条件分岐を記述

// レデューサーの作成
function countReducer(state = state_value, action) {
    switch (action.type) {
  //typeがINCREMENTだった場合の処理
        case 'INCREMENT':
        return {
            count:state.count + 1,
        };
        default:
        return state;
    }
}

Reducerは条件分岐
action.typeの値をチェックして
それぞれの値に応じて処理を行なってreturnする

Storeの作成

createStoreという関数で作成する
引数にはReducerを指定

export default createStore(countReducer);

App.js

import React from 'react';
import { connect } from 'react-redux';
import '../App.css';
import Message from './Message';
import Button from './Button';

class App extends React.Component {

  render() {
    return (
      <div>
        <h1>Redux</h1>
        <Message />
        <Button />
      </div>
    );
  }
}

export default connect()(App);
Storeのconnect

App, Message, Buttonの3つのComponentに記述している
connectはComponentにStoreを接続するための関数
connent(設定する内容)(Component);のように記述
1つ目の()には、接続する際にstateなどに設定する値を用意
2つ目の(には、コネクトするコンポーネントを用意

export default connect()(App);

App.jsではStoreの値は利用しないので,
1つ目の()の引数は何も無し

Message.js

import React from 'react';
import { connect } from 'react-redux';
import '../App.css';

// カウント表示のコンポーネント
class Message extends React.Component {

    render(){
      return (
        <p>
          {this.props.count}
        </p>
      );
    }
  }
  // ストアのコネクト
  export default connect((state)=>state)(Message);
Storeのconnect

1つ目の()の引数でstateをそのまま返すアロー関数を指定して
Messageコンポーネントにコネクトし、それをそのままexport defaultすることで
Messageをimportしたら、コネクトされたMessageが使えるようになる

  export default connect((state)=>state)(Message);

Button.js

import React from 'react';
import { connect } from 'react-redux';
import '../App.css';

class Button extends React.Component {

    constructor(props){
      super(props);
      this.doAction = this.doAction.bind(this);
    }
  
    // ボタンクリックでディスパッチを実行
    doAction(e){
        this.props.dispatch({ type:'INCREMENT' });
    }
  
    render(){
      return (
        <button onClick={this.doAction}>
          click
        </button>
      );
    }
}

  // ストアのコネクト
export default connect()(Button);
dispatch

dispatchはReduxにactionを送る働きをする
dispatchによってactionが送られると、StoreのReducerが呼び出されて
そこで、必要な処理が実行される
actionのtypeの値をチェクしINCREMENTの時はcountを+1する

this.props.dispatch({ type:'INCREMENT' });

最後に

今回のソースコードはこちらにもあります
GitHub - TakaShinoda/CountUP