Help us understand the problem. What is going on with this article?

Redux 基礎:Reducer 編

More than 3 years have passed since last update.

概要

React + Redux のサンプルを使って、Reducer の作成と動作の確認をします。

準備

Action 編の続きに modal のクラスを追加したコード(tag/init_reducer)から行います。
※ 実装完了はtag/reducer

メッセージダイアログを表示する Reducer の作成を行います。

Reducer とは

Reducer は現在の state と Action から、新しい state を生成します。

Reducer
(previousState, action) => newState

Reducer 自体は function ですが、Reducer 名がそのまま state 名になります。

function errorMessage(state = null, action) {
    :
  return newState;
}

state : {
  errorMessage: newState,
  otherStates: ...
}

Reducer は新しい state を生成してください。 前の state を変更してはいけません。

newState
function reducer(state = null, action) {
  return Object.assign({}, 
      state, 
      {
        completed: true
      });
}

Action はすべての Reducer に送られるため、関係のない Action の場合は現在の state を返してください。

function reducer(state = null, action) {
  if (action.type != ACTION_A) {
    return state;
  }
    :
  return newState;
}

よく使うパターン

配列の追加

[...state.values, newValue]

配列のある要素の変更

[
 ...state.slice(0, action.index),
 modifiedValue,
 ...state.slice(action.index + 1)
]

オブジェクトの変更

Object.assign({}, 
  state, 
  {
    completed: true,
    name: "new name"
  });

Reducer の作成

MessageModal クラスは title. message, visibility を持った modal オブジェクトをとるので、表示するだけの Reducer を作ってみます。

reducers/index.js
function messageModal(state = null, action) {
  return {
    visibility: "show",
    title: "Sample Modal",
    message: "Lorem ipsum dolor sit amet, consectetur adipisicing elit,"
  }
}

これを実行すると、何も起きませんが右のペーンを見ると、messageModal state に title. message, visibility が設定されていることがわかります。

ダイアログが表示できるように、containers/App.js を修正し state を props に設定します。

containers/App.js
import MessageModal from "../components/MessageModal.js"

   :

  render() {
    const { children, inputValue, messageModal } = this.props;
    return (
      <div>
        {this.renderErrorMessage()}
        {this.renderSample()}

        <MessageModal modal={messageModal} onPrimaryButton={() => {}}/>
        {children}
      </div>
    );
  }

   :

function mapStateToProps(state) {
  return {
    messageModal: state.messageModal,
    errorMessage: state.errorMessage,
    inputValue: state.router.location.pathname.substring(1)
  };
}

実行するとメッセージダイアログが表示されます。

Action による処理

Reducer が state を作ってダイアログが表示できるようになったので、今度は Action によって表示・非表示をコントロールできるようにします。

まず、Action を作成します。

actions/index.js
export const SHOW_MODAL = "SHOW_MODAL";
export const HIDE_MODAL = "HIDE_MODAL";

export function showModal(title, message) {
  return {
    type: SHOW_MODAL,
    title: title,
    message: message
  }
}

export function hideModal() {
  return {
    type: HIDE_MODAL
  }
}

ダイアログはデフォルトでは非表示なので、初期値を次のようにします。

reducers/index.js
const messageModalInitial = {
  visibility: "hide"
};

function messageModal(state = messageModalInitial, action) {
   :
}

typeSHOW_MODAL, HIDE_MODAL のときに新しい state を作ります。

reducers/index.js
function messageModal(state = messageModalInitial, action) {
  const { type, title, message } = action;

  switch (type) {
    case ActionTypes.SHOW_MODAL:
      return {
        visibility: "show",
        title: title,
        message: message
      };

    case ActionTypes.HIDE_MODAL:
      return {
        visibility: "hide"
      };

    default:
      return state;
  }

関係のない action に対しては何も行わないので、default では現在の state をそのまま返します。

ここで実行すると、初期状態が hide なのでダイアログが表示されなくなります。また、右のペーンで visibilityhide になっていることが確認できます。

ボタンを追加して、showModal を呼びます。

containers/App.js
    :

  handleShowModal(e) {
    const title = "Sample Modal";
    const message = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut
      labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
      aliquip ex ea commodo consequat.
      Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`;
    this.props.showModal(title, message);
    e.preventDefault();
  }

    :

        <button type="button" className="btn btn-primary btn-lg" onClick={::this.handleShowModal}>
          show modal
        </button>

show modal ボタンを押すと state が更新されダイアログが表示されます。

同じように close ボタンで hideModal が呼ばれるように修正します。

containers/App.js
   :

  handleModalPrimaryButton(e) {
    this.props.hideModal();
    e.preventDefault();
  }

   :

  <MessageModal modal={messageModal} onPrimaryButton={::this.handleModalPrimaryButton}/>

   :

最後に

Reducer は state の形が決まっていれば実装は割と簡単だと思います。
ただ、Redux や flux では同じ state のときには同じ表示になることを意識する必要があり、UI の表示が決められるような state を設計するのが重要になります。

コンポーネントとかモジュールとかいうとメソッドが呼ばれてそ動くことが多かったので、state を中心にして state に従って UI に反映するというのは考え方を変えていかないといけないと感じました。

参考

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした