95
99

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ReactのプロジェクトにReduxを導入する。

Last updated at Posted at 2017-05-07

利用プロジェクト

React-Todo

Flux

Reactだけだと、データが大規模になりソースが肥大化してしまう欠点があります。
それらを解消するため、Facebookが編み出したデザインパターンをFluxと言います。

今回はそのFluxフレームワークの一つであるReduxを主軸に
規模が大きくなりデータの管理が難しくなったReactのプロジェクトに
Reduxを導入するところを記載します。

Reduxデータフロー

Action(アクション)

Viewから呼ばれるStateに関する関数群
それぞれ、該当するReducerを呼ぶ。

Reducer(レジューサ)

Actionを受け取り、それに従って書き換えるStateの中身を作る。
ここでは、Stateは書き換えない。

Dispatch(ディスパッチ)

Actionを呼ぶ際に利用され、Dispatchを通すことでStateの更新が可能になります。

Store(ストア)

ActionとReducerを結びつける。dispatchを通してReducerから受け取ったStateのコピーの内容でStateを書き換える。

Container(コンテナ)

Storeが持つStateをReactのプロパティとしてViewと結びつける役割を持つ。

View(ビュー)

Reactのコンポーネントの部分、Stateを表示する。

Reduxの導入手順

1. ライブラリをインストールする。

必要なものは、以下の2点です。

  1. Redux
    Redux本体
  2. React-Redux
    ReactとReduxを結びつけるライブラリ

コマンドでインストールします。

# Redux
# npm
npm install redux
# yarn
yarn add redux

# React-Redux
# npm
npm install react-redux
# yarn
yarn add react-redux

2. ReactとReduxを結びつける。

まず、最初の手順としてReactとReduxを結びつけます。
記述するのはindex.jsxです。

index.js
// ./index.jsx
import React from 'react'
import ReactDOM from 'react-Fdom'
// ①
import { createStore } from 'redux'
import { Provider } from 'react-redux'

// ② 4の項目で解説
import App from './containers/App'

// ③ 次の項目で解説
import reducer from './reducers/reducer'

// ④
const store = createStore(reducer)

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

①. ReduxのcreateStore, React-ReduxのProviderをインポートします。
②. 4の項目で解説しますが、作成したAppのContainerをインポートします。
③. 次の項目で解説しますが、作成したReducerをインポートします。
④. reducerを元に、ReduxのcreateStoreでStoreを作成します。
⑤. ProviderにstoreをPropsとして渡し,App(Component)とStoreを結びつけます。

3. Reducerを作成する。

次に、書き換えるStateの中身を作成するReducerを作成します。

reducer.js
// ./reducers/reducer.jsx
// 初期State
const initialState = {
  tasks: [],
  text: '',
}
// Reducer処理
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADDTASK': {
      return console.log(action.value)
    }
    case 'ENDTASK': {
      return console.log(action.value)
    }
    case 'UPDTASK': {
      return console.log(action.value)
    }
    case 'DELTASK': {
      return console.log(action.value)
    }
    default: {
      return state
    }
  }
}

export default reducer

Reducerの最初はStateの初期値です。
今回のReact-Todoでは、

  1. ADD: Todoの追加
  2. END: Todoの完了
  3. UPD: Todoの更新
  4. DEL: Todoの削除
    以上、4つのState変更があります。
    ReducerはActionから「ActionType」というものを引数で受け取り、その中身でSwitchケースの中の該当する処理を行います。
    今はまだ処理を書かず、return nullとしていますが、
    例えばADDTASKでは、現状のStateの追加するTodoを追加する処理を記述します。
    (なんとも説明が下手なのですが....)
    ざっくりいうと、State自体は書き換えずにStateを更新した際の値をreturnする処理を記述します。
    この項で作成したreducerを前の項のreducerとしてimportしましょう。

4. Containerを作成する。

ReduxのStoreが持つStateをReactのコンポーネントが利用できるようにするため、Containerを作成します。

app.js
// ./containers/App.jsx
import {
  connect,
} from 'react-redux'

import App from './../components/App'

const mapStateToProps = (state) => {
  return state
}

export default connect(mapStateToProps)(App)

React-Reduxは、connectというプロパティを持っています。
mapStateToPropsは、全体的なStateの中から利用するStateを持ってきてくれる関数です。
正直、中でどう動いているのか、どういう意味なのかははっきりは僕もわかりません。
なので、これはお作法だ、と考えています。

最後の行、connectを利用して、mapStateToPropsが取ってきたStateの値をAppで利用できるように繋げています。

containerに関しては、ほとんどこれだけです。
大規模になればなるほど、mapStateToPropsで利用するStateが多くなります。

5. Actionを作成する。

Actionsには、Reducerへ渡すActionTypeとStateへ追加、または更新する値を渡します。

AppActions.js
// ./actions/AppActions.jsx
const Actions = {
  addTodo(value) {
    return {
      type: 'ADDTASK',
      value,
    }
  },
  fixTodo(value) {
    return {
      type: 'FIXTASK',
      value,
    }
  },
  updTodo(value) {
    return {
      type: 'UPDTASK',
      value,
    }
  },
  delTodo(value) {
    return {
      type: 'DELTASK',
      value,
    }
  },
}

export default Actions

各関数の中でreturnすることで、Reducerへ引数が渡されます。(returnの値)
それぞれReducerの中でaction.type , action.valueで取得できます。

addTodoであれば、typeがADDTASKなので
Reducer内のADDTASKの処理を行います。

valueは、Componentから渡される値です。

Actionに記載している関数一つ一つをActionCreatorと呼びます。

Reduxのデータフロー

以上でReduxの導入で必要なファイル群の作成は完了しました。
続いて実際に、Actionを読んで、Reducerが値を作成し、StoreがStateを書き換えるという流れを記載、もとい現状の処理をReduxへ書き換える処理を記述していきます。

1. ActionCreatorを呼ぶ

ReactのViewからReducerを呼ぶにはdispatchが必要です。
そのため、Containerに以下の記述を行います。

App.js
// ./containers/App.jsx
import {
  connect,
} from 'react-redux'

import App from './../components/App'
import Actions from './../actions/AppActions'

const mapStateToProps = (state) => {
  return state
}
const mapDispatchToProps = (dispatch) => {
  return {
    handleTodoAdd(value) {
      dispatch(Actions.addTodo(value))
    },
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

ActionsのaddTodoをインポートし、mapDispatchToPropsの中で
handleTodoAddの関数内でdispatchを用いてaddTodoを呼ぶようにします。
それらをconnectを利用してAppへPropsを渡します。
以後、ActionCreatorは同じような形で記述して行きます。

作成したActionCreatorは、以下の記述でReact側から呼び出しが可能です。

App.js
// ./components/App.jsx
...
  AddTasks() {
    if (!(this.state.text)) {
      return
    }
    this.props.handleTodoAdd(this.makeTasks(this.state.text))
  }
...

タスクを追加する部分です(makeTasksというのは、タスクにidをつけたりする関数です。)
containerからPropsとしてStateとActionCreatorが渡されるため、this.propsでアクセスします。
this.stateというのは、基本的に面倒なhandleChangeで変化するStateです(個人的にhandleChangeといったものはRedux側で管理したくないので、残しています。)

2. Reducerの記述

ActionCreatorの作成、呼び出しは完了しました。
それでは実際に、書き換えるStateを作成するReducerを記述します。

reducer.js
// ./reducers/reducer.jsx
const initialState = {
  tasks: [],
}

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADDTASK': {
      return {
        tasks: state.tasks.concat(action.value),
      }
    }
    case 'ENDTASK': {
      return {
        tasks: action.value,
      }
    }
    case 'UPDTASK': {
      return {
        tasks: action.value,
      }
    }
    case 'DELTASK': {
      return {
        tasks: action.value,
      }
    }
    default: {
      return state
    }
  }
}

export default reducer

まだ、ADDTASKの部分だけですが、stateが現在のStateになります。
それに、concat(結合)を利用することで現在のStateを更新することなく、Storeへタスクを追加した値を渡すため書き換えることが可能です。
オブジェクトの場合はObject.Assignとかを利用します。

これで、React側でthi.propsよりhandleTodoAddを呼び出すことで、ActionCreatorのaddTodoが呼び出されStateのTasksに新しいTodoが追加されます。

React-Todo Redux版

下記のリポジトリでご確認ください。
React-Todo Redux版

後書き

Reactはその特性上、いろんなことができそうですが
大規模な案件、子コンポーネントが増えてしまうとそれだけStateが増えてしまいReduxの導入が必須になってきます。
導入してみれば意外と簡単で、何より、ActionCreatorを呼ぶことでいわば、どこからでもStateの更新が可能なのでとても便利です。
小規模な場合は問題ないかもしれませんが、大規模なプロジェクトでは導入をオススメします。

95
99
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
95
99

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?