LoginSignup
14
22

More than 5 years have passed since last update.

【上っ面だけでも】Redux【理解したい】

Posted at

コードで理解するRedux(React使用)
を理解するために、自分なりの解釈をまとめた記事です。

Redux追加 環境作成

npm install --save-dev redux react-redux

Redux三原則をもっと簡単にしてみる

①コンポ単位でstate管理するのだりーよ!一個に集約しようぜ!
Storeという管理者におまかせするようにした。

②state触りたいならちゃんと手順踏んでね
バグが特定しやすくなる。

③reducerは同じデータなら毎回同じデータを返すこと
すいません、ここだけ理由ができていません。

登場人物と役割

参考
Redux + React (Example: Todo List) の構成図
Reduxでコンポーネントを再利用する

説明
Store
(ストア)
stateの管理者。

アプリ内で一つしか存在しない。
Provider
(プロバイダ)
Storeと他コンポーネントの仲介役。

react-reduxが提供するコンポーネントで、アプリ内で一つしか存在しない。
Container
(コンテナ)
stateの変更通知を受け取って、propsを子に渡してあげるのが主な目的。

渡すpropsには、さり気なくdispatch(後述)を仕込むのがミソ。
Presentatinal
(プレゼンテーション)
描画特化。

①propsを使ってDOMの描画。
②ユーザから要求がアレば、propsとして受け取った関数の呼び出し。

ただコレだけを行う。

Reduxの流れを知る

まず、なんとか理解してほしいこと

  • state(画面の状態)はStoreが管理する
  • stateの変更はStoreにある「Reducer」という場所で行う
  • Reducerはdipatchという行為で実行される(つまり、dispatchする=Reducerの起動)
  • dispatchをするにはActionと呼ばれる物が必要(処理分岐で使うだけです)
  • Reducerは新しいstateを用意するのでコンポーネントはそれを描画してあげる

今まで出てきた言葉を使って、Reduxをする

①画面でユーザーの要求が発生(stateの変更要求)
②Containerにあるイベントが実行
③イベントはReducerを起動するためにdispatchする(Actionの作成もここで)
④Reducerはstate(要求時の状態)とActionから、新しいstate(要求後の状態)をContainerに渡す
⑤Containerはstateをpropsに変換して、画面で描画する
⑥終わり

Redux(1).jpg

コード

ボタン押すたびにカウントアップするだけの処理。
役割ごとにファイルで分けてみました(コレがベストかは別にして)

処理の流れを踏まえた、実装までのコーディング方法

目的 どこで なにを
React下準備 React描画部 ReactDOM.renderで指定できるところを用意。
Store作成 Store createStore関数でStoreを作成。

引数で必要なReducerとstate初期化関数は、中身がなくてもいいので定義しちゃいましょう。
Providerを用意 Provider監視部 作成したStoreをProviderのプロパティにセットして、JSXに記載。
メインViewを用意 Presentatinal 中身はなくてもいいので、イベントハンドラ定義まで進める。
イベントハンドラでよぶ関数を準備 Container stateが変わるなら、Reducerに仕事をお願いしないといけないので、関数内でdispatchの呼び出しを実装。
Actionの作成 Container dispatchの呼び出しに必要なので、関数を定義。
Reducerの実装 Store 作成途中だったReducerに、Actionと既存のstateから、どう新しいstateを返すか定義。
新しいstateをpropsに変換 Container 関数実装。
コンテナのイベントを呼び出す Presentatinal イベントハンドラ内でContainerで定義したイベントを追記。
変換したpropsを用いて描画 Presentatinal JSXに追記。
(state = props)の初期値に対応 Store state初期化関数の中身実装。
PresentatinalとContainerの紐付け Container connect関数の実装。
コンテナをProviderの監視下に Provider監視部 JSXにContainerを追記。

実例

React描画部

index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

Provider監視部

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import store from './Store.js';
import Container from './Container.js';

//①唯一のStoreを含むProviderが、コンポーネントを包むように記載
//②stateを切り離されたコンポーネントたちを下層で定義
ReactDOM.render(
    <Provider store={store}>
      <Container />
    </Provider>
    ,
    document.getElementById('root')
);

Store

Store.js
import { createStore } from 'redux'

//Reducer
function reducer(state, action) {
  //Actionに応じて、処理分岐
  switch(action.type) {
    case 'ACT1':
      //新しいstateを作成
      //return先は、コンテナのprops変換処理かな?(未確認)
      return (
        Object.assign(
          {}                                  //空のオブジェクトに以下を追加
          , state                             //既存state
          , {count: Number(action.count) + 1} //別途変更がある箇所
        )
      );
    default:
      return state;
  }
}

//state初期化
const initialState = {
  count: 0
};

//Store定義(初期stateとReducerのセット)
export default createStore(reducer, initialState);

Presentatinal

App.js
import React, { Component } from 'react';

// プレゼンテーション層
export default class App extends Component {
  //イベントハンドラ
  eClick(event) {
    //コンテナでdispatchする
    this.props.goDispatch(event.target.value);
  }
  render() {
    return (
      <div>
        {/* 押されるたびにカウントアップするボタン */}
        <button value={this.props.count} onClick={this.eClick.bind(this)}>ClickMe</button>
        <br />
        {this.props.count}
      </div>
    );
  }
}

Container

Container.js
import { connect } from 'react-redux'
import App from './App.js';

//イベントをpropsで使うために定義
function addProps(dispatch) {
  return {
    //propsで参照できるイベントを定義
    goDispatch(count) {
      //stateが変更させたいので、イベント発火でReducerを起動させる
      dispatch(actOne(count));
    }
  };
}

//Action作成
function actOne(count) {
  return {
    type: 'ACT1', //Reducerで、「action.type」で参照
    count         //Reducerで、「action.count」で参照
  };
}

//state->props変換
function mapStateToProps(state) {
  return {
    count: state.count
  };
}

//プレゼンテーション層に、変換ルールとイベントハンドラを紐付け
export default connect(
  mapStateToProps
  ,addProps
)(App);
14
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
22