あいつの日誌β

働きながら旅しています。

React tutorial (6)

前回は react での routing について触れました。 今回は HTTP 通信が成功した時などに表示する Notification (Flash Message) に触れます。

Notification も色々あるのですが豊富な機能を備えた reapop を紹介します。

github.com

目次

  • 開発環境を準備
  • React の基本的な Life Cycle に触れる
  • redux に触れる
  • redux-saga に触れる
  • react router に触れる
  • npm で公開されている components を導入して echo system を体感する <= 今日やること
  • redux-form に触れる
  • react-select に触れる

今日やること

f:id:okamuuu:20170124213115g:plain

準備

依存モジュールを install します。 redux-thunk は reapop が使うので install しますが今回のチュートリアルでは自身で実装する箇所はありません。

npm install --save redux-thunk font-awesome reapop reapop-theme-wybo

各種ファイル

edit src/store/configureStore.js

import { createStore, applyMiddleware } from 'redux'
import { reducer as notificationsReducer } from 'reapop'
import createSagaMiddleware from 'redux-saga'
import createLogger from 'redux-logger'
import thunk from 'redux-thunk'

import rootReducers from '../reducers'
import rootSaga from "../sagas"

const sagaMiddleware = createSagaMiddleware()
const logger = createLogger()

const middleware = [thunk, sagaMiddleware, logger]
const createStoreWithMiddleware = applyMiddleware(...middleware)(createStore)

export default function configureStore(initialState) {
  const store = createStoreWithMiddleware(rootReducers, initialState)
  if (module.hot) {
    module.hot.accept('../reducers', () => {
      const nextRootReducer = require('../reducers').default
      store.replaceReducer(nextRootReducer)
    })
  }
  sagaMiddleware.run(rootSaga)
  return store
}

edit src/reducers/index.js

import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'
import { reducer as notificationsReducer } from 'reapop'

import { FETCH_SUCCESSED } from '../actions'

const fetchInitialState = {
  posts: []
}

function fetch(state = fetchInitialState, action) {
  switch (action.type) {
  case FETCH_SUCCESSED:
    return Object.assign({}, state, action.payload)
  default:
    return state
  }
}

const rootReducer = combineReducers({
  notifications: notificationsReducer(),
  fetch,
  routing: routerReducer,
})

export default rootReducer

edit src/containers/Posts.js

import React, {Component} from 'react'
import { connect } from 'react-redux'
import Notifications from 'reapop'
import theme from 'reapop-theme-wybo'

import { fetchRequest } from '../actions'
import { PostItem } from '../components/Posts'
import { getPosts } from '../actions'

class Posts extends Component {

  handleButtonClick() {
    this.props.dispatch(getPosts({userId: 1}))
  }

  render() {
    const {posts} = this.props.fetch

    return (
      <div className="container">
        <Notifications theme={theme}/>
        <h2>Posts</h2>
        {posts.map((post) => (
          <PostItem key={post.id} post={post} />
        ))}
        <button className="btn btn-default" onClick={this.handleButtonClick.bind(this)}>Push</button>
      </div>
    )
  }
}

const select = state => (state)

export default connect(select)(Posts)

edit src/sagas.js

import { takeEvery } from "redux-saga"
import { put, call } from "redux-saga/effects"
import { addNotification as notify } from 'reapop'

import {
  FETCH_REQUESTED,
  fetchSuccessed,
  fetchFailed,
} from "./actions"

function notifySuccess(message) {
  return notify({
    message,
    position: 'tc',
    status: 'success',
    dismissAfter: 3000
  })
}

function notifyError(message) {
  return notify({
    message,
    position: 'tc',
    status: 'error',
    dismissAfter: 3000
  })
}

function* fetch(action) {
  try {
    const data = yield call(action.payload)
    yield put(fetchSuccessed(data))
    yield put(notifySuccess("fetch success"))
  } catch (err) {
    yield put(fetchFailed(err))
    yield put(notifyError(err.message))
  }
}

export default function* rootSaga() {
  yield takeEvery(FETCH_REQUESTED, fetch)
}

説明

fetch が成功したらメッセージを表示させます。 今回の tutorial ではこのような書き方をしていますが、 redux-saga を使っているので FETCH_SUCCESSEDFETCH_FAILED を検知して動作させることもできます。

次回は redux-form に触れます。