Webデザイナーでもできる!React + Redux + ES6でプロジェクトを始めるときの簡単なチュートリアル

react

前回の記事でReact + Redux + ES6をwebpackで動かすところまでやりました。初めてRedux + Reactに触る方を対象に前回の「Hello World」でテキスト表示までやったところからReduxでStateを変更・管理しViewに反映させるところまでやってみたと思います。

Ads

おさらい

以前に書いた下記の記事では、React + Redux + ES6のプロジェクトをWebPackで「Hello World」を表示させるところまでやりました。

こちらで使ったファイルを使用するので、わからない方は一旦目を通していただけるとよりわかりやすくなります。

今回はStateの変更をしてViewに反映させるところまでやってみたいと思います。Stateの変更についてはWebデザイナーにも馴染みのあるモーダルを使って紹介したいと思います。

Redux とは

ReactJSのStateを管理するためのフレームワークです。React + ReduxではReactがViewをReduxがState(状態)を管理するように分担されます。ちなみに、ReduxはAngularJSやjQueryなどと併せて使用することもできます。ただ、Reactとの相性が一番いいです。

Reduxのデータフローについて

とりあえず流れとしては

View → Action → Middleware → Reducer → Store → View

これを覚えなければ今現状でStateはどこにいるのかわからなくなってしまいます。
ということで以前記事を参考にしていただけるといいかなと思います。

ディレクトリ変更

Reduxに合わせてディレクトリを変更します。

src
  ├─ /actions
  ├─ /components
  ├─ /containers
  ├─ /reducers
  ├─ /store
  └─ index.js

index.jsは前回のファイルを使っています。

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import configureStore from './store/reduxStore';
import App from './containers/App'

const store = configureStore()

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

modal 作ってみる

では何かしら作った方がデータの流れが把握しやすいので、今回はmodalを作ってみましょう。ざっくりした流れでいうと・・・

ボタンクリック → Stateが変更されることが伝わる → State変更 → modal出現

っていう感じです。もう少しだけ詳しくお話しすると、ボタンクリックの時点ではState(状態)modal非表示です。要は非表示ステータス。そしてState変更に伴い表示ステータスになります。そうすることでView側でmodalが出現する仕組みを作ります。

cssは既存のものをとりあえず使います

ボタンやモーダルのデザインだったり、見た目に関してはCSSを使います。なんでもいいんですが、以前僕が作ったフレームワークで今回は対応しようかなと思います。

ダウンロードはこちら

publicディレクトリ内に配置します。さらにindex.htmlから読み込みできるようにしておきましょう。

root
  ├─ public
  |     ├─ app.css
  |     └─ indax.html
  └─ src

ActionTypes.js

src/constants/ActionTypes.jsを作成します。

export const MODAL_OPEN = "MODAL_OPEN"

Modal.js action

src/actions/Modal.jsを作成します。

import * as ActionTypes from '../constants/ActionTypes'

export function modalOpen(dec) {
  return {
    type: ActionTypes.MODAL_OPEN,
    show: dec
  }
}

dectruefalseで渡ってきます。これとtypeMODAL_OPENをreducerに渡してstateの状態を変えます。

Modal.js reducers

src/reducers/Modal.jsを作成します。

import * as ActionTypes from '../constants/ActionTypes'

export default function modalReducer(state={show: false}, action) {
  switch(action.type){
    case ActionTypes.MODAL_OPEN:
      return {show: action.show}
    default:
      return state
  }
}

action.showに先ほどactionで作ったshowが入っているのでstate.showに渡します。

RootReducer.jsにModalを連携

RootReducer.jsにModalを連携することでcomponentやcontainersのView側でmodalを使うことができるようになります。

import {combineReducers} from "redux"
import Modal from "./Modal"

const App = combineReducers({
  Modal,
})

export default App

Modal.js components

src/components/Modal.jsを作成

showではtruefalsecontaiers/App.jsが渡されてきます。それをaria-clicked={show === true ? 'true' : 'false'}でスタイルを変更しています。

なので、CSS側では

// ・・・省略
.c-modal {
  position: fixed;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 99999;
  overflow-y: scroll;
  opacity: 0;
  display: none;
  animation-duration: 0.5s;
  animation-name: fade-out-100;
  -webkit-animation-duration: 0.5s;
  -webkit-animation-name: fade-out-100;
}

// ・・・省略

.c-modal.is-fadein, .c-modal[aria-clicked="true"] {
  display: block;
  opacity: 1;
  animation-duration: 0.5s;
  animation-name: fade-in-100;
  -webkit-animation-duration: 0.5s;
  -webkit-animation-name: fade-in-100;
}

// ・・・省略

というように.c-modalではdisplay: none;になっていて、.c-modal[aria-clicked="true"]display: block;になります。これでCSS側でもstateの変更に応じてスタイルを変更できるようになりますね。

次に{title}{children}ですがこれらもcontaiers/App.jsから渡されてきます。これらは後ほど説明します。

import React from 'react'

export default class Modal extends React.Component{
  handleClickOpen(){
    const { handleModalOpen } = this.props
    handleModalOpen()
  }

  handleClickClose(){
    const { handleModalClose } = this.props
    handleModalClose()
  }

  render(){
    const {show, title, children} = this.props
    return(
      <div className="c-modal" aria-clicked={show === true ? 'true' : 'false'}>
        <div className="c-modal__panel">
          <div className="c-modal__header">
            <h3>{title}</h3>
          </div>
          <div className="c-modal__content">
            {children}
          </div>
          <div className="c-modal__bottom">
            <button className="c-btn c-btn-default--flat" onClick={this.handleClickClose.bind(this)}>閉じる</button>
          </div>
        </div>
        <div className="c-modal__overlay" onClick={this.handleClickClose.bind(this)}></div>
      </div>
    )
  }
}

App.jsの変更

src/containers/App.jsを変更します。

import Modal from '../components/Modal'でcomponentのModalを

import {modalOpen} from '../actions/Modal'でactionのModalをimportしています。

function mapStateToProps(state) {
  return {
    show: state.Modal.show
  }
}

ここでmodalのstateが変更されたことを受け取ります。ちなみにmapStateToPropsとあるようにViewではstateをpropsに渡して使うようになります。

const {show} = this.propsでrender部分で初めてstateのshowを使えるようになります。そして、

<Modal
  handleModalOpen={this.handleModalOpen.bind(this)}
  handleModalClose={this.handleModalClose.bind(this)}
  show={show}
  title='modalテスト'
>
  モーダル内容
</Modal>

こちらでshowプロパティにthis.props.showを渡すことでcomponentのModalで受け取ることができますね。それと同じような感じでtitleプロパティもありますね。こちらはstaticのテキストですが、こうしておくことでいろんなmodalで使い回すことができるようになりますね。

あとはcomponentのところで少し紹介した{children}です。これはcontainers/App.jsには書かれていないですね。実はこれはモーダル内容の部分がchildrenになります。

<Modal>
ここがchildren
</Modal>

という感じです。今回はテキストでやっていますが、HTML要素だったり、なんでもchildrenに入れることができます。なので、ページによって違うモーダルにしたりできますね。

import { bindActionCreators } from 'redux'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import Modal from '../components/Modal'
import {modalOpen} from '../actions/Modal'

class App extends Component {
  componentWillMount() {}

  handleModalOpen(){
    this.props.modalOpen(true)
  }

  handleModalClose(){
    this.props.modalOpen(false)
  }

  render() {
    const {show} = this.props
    return (
      <div>
        <div className="l-wrapper">
          <div className="c-container">
            <h1 className="c-title c-title--primary">Modal</h1>
            <button className="c-btn c-btn-primary--flat" onClick={this.handleModalOpen.bind(this)}>Modal Open</button>
            <Modal
              handleModalOpen={this.handleModalOpen.bind(this)}
              handleModalClose={this.handleModalClose.bind(this)}
              show={show}
              title='modalテスト'
            >
              モーダル内容
            </Modal>
          </div>
        </div>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
    show: state.Modal.show
  }
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators(Object.assign({}, {modalOpen}), dispatch)
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

reduxStore.js

src/store/reduxStore.jsの作成

import {createStore,  applyMiddleware} from 'redux'
import RootReducer from '../reducers/RootReducer'

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

ここまで出来ればおそらくブラウザで表示できるようになっているはずです。

npm start

してからlocalhost:4000で確認してみます。おそらく間違っていなければボタンが表示され、ボタンをクリックするとモーダルが出現していると思います。

環境を整える

ここまで作って少し慣れてくると現在のStateの状態をつかむことが大事になってきます。そのために、便利なnpmモジュールがいくつかあるので紹介します。

redux-logger

redux-logger

Stateがどう変わったか、及びどのActionがdispatchされたことによるか、をログとして表示してくれるMiddlewareです。今回のモーダルくらいだと足した恩恵を受けられないけど、APIと連携したりし始めるとあると便利!!

prevStateやnextStateも表示されるし、変更があったタイミングでその都度ログに出してくれるので僕は日常的にこちらを使っています。

redux-devtools

こっちの方がより高機能です。Resetボタンなども用意されていてStateを初期状態にしたりすることもできます。ただ開発が進んでくると重くなる。

自分的にはあると便利!!Redux-formとか使い始めるとちゃんとデータが渡っているか確認したりするのに使っていました。

どっちかでもいいんだろうけど自分的にはある程度棲みわけされている印象。

ファイルをちょっと変える

package.json

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "webpack-dashboard -- webpack-dev-server -d --hot --inline --port 4000",
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack-dashboard -- webpack -d --watch"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/Takumi0901/react-redux-webpack.git"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.13.2",
    "babel-loader": "^6.2.4",
    "babel-preset-es2015": "^6.13.2",
    "babel-preset-react": "^6.11.1",
    "redux-devtools": "^3.0.1",
    "redux-devtools-dock-monitor": "^1.0.1",
    "redux-devtools-log-monitor": "^1.0.2",
    "redux-logger": "^2.6.1",
    "webpack": "^1.13.1",
    "webpack-dashboard": "^0.1.8",
    "webpack-dev-server": "^1.14.1",
    "webpack-init": "^0.1.1"
  },
  "dependencies": {
    "react": "^15.3.0",
    "react-dom": "^15.3.0",
    "react-redux": "^4.4.5",
    "redux": "^3.5.2"
  }
}

この辺も変わっています

"scripts": {
    "start": "webpack-dashboard -- webpack-dev-server -d --hot --inline --port 4000",
    "test": "echo \"Error: no test specified\" && exit 1",
    "watch": "webpack-dashboard -- webpack -d --watch"
  },

ここまで変更できたら

npm update

store/reduxStore.jsstoreも変更します。

import {createStore,  applyMiddleware} from 'redux'
import RootReducer from '../reducers/RootReducer'
import createLogger from 'redux-logger'
import DevTools from '../containers/DevTools'

export default function configureStore() {
  const logger = createLogger({logger:console})
  const createStoreWithMiddleware = applyMiddleware(
      logger
  )(createStore)
  const store = createStoreWithMiddleware(RootReducer, DevTools.instrument());
  return store
}

DevToolsはViewに表示をさせるので以下のファイルも必要です。

containers/DevTools.jsを作成する。

import React from 'react'
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'

export default createDevTools(
    <DockMonitor toggleVisibilityKey="ctrl-h"
                 changePositionKey="ctrl-w">
      <LogMonitor />
    </DockMonitor>
)

containers/App.js

import DevTools from './DevTools'

まずDevToolsをimportします。
次に<DevTools />で表示させます。

render() {
    const {show} = this.props
    return (
      <div>
        <div className="l-wrapper">
          <div className="c-container">
            <h1 className="c-title c-title--primary">Modal</h1>
            <button className="c-btn c-btn-primary--flat" onClick={this.handleModalOpen.bind(this)}>Modal Open</button>
            <Modal
              handleModalOpen={this.handleModalOpen.bind(this)}
              handleModalClose={this.handleModalClose.bind(this)}
              show={show}
              title='modalテスト'
            >
              モーダル内容
            </Modal>
          </div>
        </div>
        <DevTools />
      </div>
    )
  }
}

webpack-dashboard

redux-devtools

コンソールをカッコ良くすることができます。まあ、正直やらなくてもいいかなー

webpack.config.jsを変更

var DashboardPlugin = require('webpack-dashboard/plugin');
module.exports = {
  entry: './src/index.js',
  output: {
    path: './public',
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
    ]
  },
  plugins: [
    new DashboardPlugin()
  ],
  devServer: {
    contentBase: 'public'
  }
}

まとめ

ある程度初心者向けに記事を書いてみました。これでmodalやTabなどUIに関わる部分についてはほぼほぼ同じような感じで作ることができると思います。

今回作成したプロジェクトは下記よりダウンロードできます

オススメの本



WebデベロッパーのためのReact開発入門 JavaScript UIライブラリの基本と活用

イチからわかる!Reactの仕組みと使い方UIコードの再利用化と速度向上を図る!Reactのコンセプト、コンポーネント、JSX、活用テクニック、一歩進んだ使い方を解説!

いいなと思ったらシェアお願いします

Ads
ページの先頭へ