JavaScript
reactjs
React
redux

初投稿ながらも、頑張って書いてみました。

小規模な開発をする上で、生のReactに不満はなかったのですが、今後のスキルアップのために「Redux」について触れていきたいと思います。

Redux について

Reactだけでも、フロントエンドの開発はできます。
Reactと独自実装でも状態管理はできますが、いい感じに状態管理周りをまとめてくれるフレームワークの一つが Redux です。

Reduxの導入

react用モジュール

・react
・react-dom

redux連携用モジュール

・redux
・react-redux

package.json
{
  "name": "react-redux-tutorial-counter",
  "version": "1.0.0",
  "description": "Sample app",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "browserify src/index.js -o dist/bundle.js -t [ babelify --presets [ react es2015 ] ]"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "browserify": "12.0.1",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babelify": "^7.2.0"
  },
  "dependencies": {
    "react": "^15.0.1",
    "react-dom": "^15.0.1",
    "react-redux": "^4.4.2",
    "redux": "^3.4.0"
  }
}

Reduxを構成するコンポーネントの種類

今回出てくるコンポーネントは下記の二つです。

①Provider
Providerコンポーネントはreact-reduxが提供する実在するReactコンポーネントです。
唯一、Storeを持つことを許された存在で、Stateやdispatch関数を配下のContainerコンポーネントで利用可能にします。

②Container
Containerコンポーネントは connect関数でReactコンポーネントをラッピングしたものです。

フォルダ構成

・dist

コンパイルしたものをここのディレクトリに出力します。

dist/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>Redux</title>
  </head>
  <body>
    <div id="root"></div>
    <script src="bundle.js"></script>
  </body>
</html>

・src

srcはactions, components, containers, reducers, storeから構成されます。

src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
import App from './containers/app';

const store = configureStore();

ReactDOM.render(
  //ストアを作ってproviderに渡す。
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

index.htmlの中の要素に流し込む。

store

src/store/configureStore.js
import { createStore } from 'redux';
import reducer from '../reducers/index';

export default function configureStore() {
    const store = createStore(reducer);
    return store;
}

Storeを作成する際に、ReducerをcreateStore()メソッドの第一引数として渡すことで、Store内でstateの更新が可能になります。
また、ReduxにはMiddlewareって概念があるらしく、dispatchの前後に行う任意の処理を追加することができる。

reducers

src/reducers/index.js
// 初期ステート設定
const initialState = {
  fuga: 0
}

// actionに応じてステート変更
export default function reducer(state = initialState, action) {
  switch (action.type) {
    case 'INCREMENT': {
      return { fuga: state.fuga + 1 }
    }
    case 'DECREMENT': {
      return { fuga: state.fuga - 1 }
    }
    default:
      return state
  }
}

state の状態を更新するためのロジックを書く部分。

actions

src/actions/app.js
export default {
  increment: () => {
    return { type: 'INCREMENT' }
  },
  decrement: () => {
    return { type: 'DECREMENT' }
  }
}

actionにより、どの状態に推移させるのか記載する。

containers

src/containers/app.js
import React from 'react'
import { connect } from 'react-redux'
import App from '../components/app'
import AppActions from '../actions/app'

function mapStateToProps(state) {
  return state
}

// clickでactionを飛ばず
function mapDispatchToProps(dispatch) {
  return {
    Increment_Click: () => { dispatch(AppActions.increment()) },
    Decrement_Click: () => { dispatch(AppActions.decrement())}
  }
}

export default connect(//これが子コンポーネントで使える要素(props)
  mapStateToProps,
  mapDispatchToProps
)(App)

mapStateToProps関数とmapDispatchToProps関数は、それぞれStoreのstateとdispatchメソッドをpropsを通して、Container Componentで扱えるようにするものです。

components

src/components/app.js
import React from 'react';
//reduxやactionsをimportしていない。→ 必要なモノは親から全てprops経由で受け取っている
export default class App extends React.Component {
  render() {
    return (
        <div>
          <span>{this.props.fuga}</span><br/>
          <button onClick={ () => this.props.Increment_Click() }>*増加*</button>
          <button onClick={ () => this.props.Decrement_Click() }>*減少*</button>
        </div>
    );
  }
}

ビルドする

npm run build

dist下にbundle.jsが生成されるので、最初に作ったindex.htmlにアクセスします。

参考にさせていただきました

React + Redux入門初歩 実際に一からアプリを構築してみる