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

ReactJS入門@ES6:Redux編

More than 3 years have passed since last update.

対象者:Reactの基本を理解している人 / Reduxについて勉強している人
前:ReactJS入門@ES6:ReactRouter編
次:未定


まえがき

Reduxの目的はStateを一元管理することです。

素のReactでは、Stateはコンポーネントが管理します。
コンポーネントの数が多くなり、親から子、子から子へとStateを継承したり、
コンポーネント間の連携などを考えだすと、Stateをどこかで一元管理し、
コンポーネントはイベントを受け、Stateを変更する命令を出すことに専念したくなります。
その仕組と制約を実現するのがReduxの役目だと、私は考えています。


まずはReduxを使わないカウンターをつくってみる

カウンターをつくりましょう、カウンター。
画面にカウント回数と、プラスボタンとマイナスボタンが表示されているだけのやつです。

class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }
  onIncrement() {
    this.setState({ count: this.state.value + 1 });
  }
  onDecrement() {
    this.setState({ count: this.state.value - 1 });
  }
  render() {
    return (
      <div>
        カウント: {this.state.count} 
        <br />
        <button onClick={::this.onIncrement}>プラス</button>
        <button onClick={::this.onDecrement}>マイナス</button>
      </div>
    )
  }
}

できました。
簡単ですね。


Reduxの前準備

さて、このカウンターをRedux化していきます。
まずRedux化に必須となるActions,Reducer,Storeを作成します。

1. Actions

まずはActionsを考えます。
Actionsとは、命令コマンドのことだと思って下さい。
さて、このカウンターではどんな命令コマンドが必要でしょうか。

  1. カウントをアップせよ
  2. カウントをダウンせよ

まあこの2つ程度ですね。
その命令コマンドをコードに書き起こします。

const INCREMENT_COUNTER = {
  type: 'INCREMENT_COUNTER'
}
const DECREMENT_COUNTER = {
  type: 'DECREMENT_COUNTER'
}

終わりです。
簡単ですね。

2. Reducer

次に、Reducerを考えます。
Reducerは、StateとActionsを受け取り、変更したStateを返すだけの処理です。
今回の場合、INCREMENT_COUNTERというActionsを受け取ったらStateのcountをプラスし、
DECREMENTの場合その逆を行うだけの処理を行います。
まあ見れば分かります。

function counterReducer(state = {count: 0}, action) {
  switch (action.type) {
    case 'INCREMENT_COUNTER':
      return {count: state.count + 1}
    case 'DECREMENT_COUNTER':
      return {count: state.count - 1}
    default:
      return state
  }
}

簡単ですね。

3. Store

さて、最後にStoreを作成しましょう。
Storeは、Stateを管理するものです。
StateはReducerが変更します。
よって、StoreはReducerから作成します。

const store = Redux.createStore(counterReducer);

終わりです。
簡単ですね。


コンポーネント変更の前準備

次にコンポーネントを変更する前準備を行います。

コンポーネントが持つStateはStoreに移動し、
ボタン押下時の処理はReducerに移動しました。
よって、コンポーネントに対し、StateとActionsを割り当てる必要があります。

function mapStateToProps(state) {
  return {
    count: state.count
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onIncrement: () => dispatch(INCREMENT_COUNTER),
    onDecrement: () => dispatch(DECREMENT_COUNTER)
  };
}

let App = connect(
  mapStateToProps,
  mapDispatchToProps
)(Counter);

これでcountがコンポーネントのStateとして使用でき、
onIncrementとonDecrementがコンポーネントから呼び出せるようになります。

あとはサクッとコンポーネントを修正します。

class Counter extends React.Component {
  render() {
    const { count, onIncrement, onDecrement } = this.props;
    return (
      <div>
        カウント: {count} 
        <br />
        <button onClick={onIncrement}>プラス</button>
        <button onClick={onDecrement}>マイナス</button>
      </div>
    )
  }
}

最後にReactDOM.renderしたら完成です。

Providerでラップ、storeの指定、
コンポーネントをconnectした結果を指定することに注意。

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

今回作成したサンプルコード

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
import { Provider, connect } from 'react-redux'

// Reactのみで構成したカウンター
class ReactCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 0
    }
  }
  onIncrement() {
    this.setState({ value: this.state.value + 1 });
  }
  onDecrement() {
    this.setState({ value: this.state.value - 1 });
  }
  render() {
    return (
      <div>
        カウント: {this.state.value} 
        <br />
        <button onClick={::this.onIncrement}>プラス</button>
        <button onClick={::this.onDecrement}>マイナス</button>
      </div>
    )
  }
}

//ReactDOM.render(
//  <ReactCounter />,
//  document.getElementById('root')
//);

/* -- -- -- -- -- ここからRedux用コード -- -- -- -- -- */

// Redux向けに再構成したカウンター
class ReduxCounter extends React.Component {
  render() {
    const { count, onIncrement, onDecrement } = this.props
    return (
      <div>
        {/* ReactCounterも一緒に描画してみる */}
        <ReactCounter />
        カウント: {count} 
        <br />
        <button onClick={onIncrement}>プラス</button>
        <button onClick={onDecrement}>マイナス</button>
      </div>
    )
  }
}

// Actions
const INCREMENT_COUNTER = {
  type: 'INCREMENT_COUNTER'
};
const DECREMENT_COUNTER = {
  type: 'DECREMENT_COUNTER'
};

// Reducer
function counterReducer(state = {count: 0}, action) {
  switch (action.type) {
    case 'INCREMENT_COUNTER':
      return {count: state.count + 1};
    case 'DECREMENT_COUNTER':
      return {count: state.count - 1};
    default:
      return state
  }
}

// Store
const store = createStore(counterReducer);

function mapStateToProps(state) {
  return {
    count: state.count
  };
}

function mapDispatchToProps(dispatch) {
  return {
    onIncrement: () => dispatch(INCREMENT_COUNTER),
    onDecrement: () => dispatch(DECREMENT_COUNTER)
  };
}

let ReduxCounterApp = connect(
  mapStateToProps,
  mapDispatchToProps
)(ReduxCounter);

// レンダリング
ReactDOM.render(
  <Provider store={store}>
    <ReduxCounterApp />
  </Provider>,
  document.getElementById('root')
);
HIGAX
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
ユーザーは見つかりませんでした