LoginSignup
4
3

More than 5 years have passed since last update.

windows + vagrant + virtualbox + ubuntu16.04 + docker で s2sのredux-sagaのサンプルを試してみたメモ

Posted at

s2sをdockerで試してみたメモ, docker + webpack-dev-server で s2sのshopping-cartのサンプルを試してみたメモに引き続き、サンプルを動かしてみる。

今回はs2s-redux-actions-sampleを使用。

動作環境

  • windows10
  • vagrant1.9.7
  • virtualbox5.1.26
  • ubuntu-16.04
  • Docker version 17.09.0-ce, build afdb6d4
  • docker-compose version 1.17.1, build 6d101fb

フォルダ構成

app
  - docker
    - s2s
      - Dockerfile
    - docker-compose.yml
  + src
  + dist

最終的なソース

docker設定

docker/s2s/Dockerfile
FROM node:9.2.0

# コンテナ上の作業ディレクトリ作成
WORKDIR /app

# ソースダウンロード
RUN git clone --depth=1 https://github.com/cndlhvn/s2s-redux-actions-sample.git

# s2sをビルド
WORKDIR /app/s2s-redux-actions-sample
RUN yarn

# chokidarのポーリング設定
RUN sed -i -e "s/\(ignoreInitial: true\)/\1,\n    usePolling: true, /g" /app/s2s-redux-actions-sample/node_modules/s2s/lib/index.js
docker/docker-compose.yml
version: '3'
services:
  s2s:
    build: ./s2s
    volumes:
      - ../src:/app/s2s-redux-actions-sample/src
      - ../dist/build:/app/s2s-redux-actions-sample/build
    command: [yarn, run, s2s]

  dev-server:
    build: ./s2s
    volumes:
      - ../src:/app/s2s-redux-actions-sample/src
    ports:
      -  8080:3000
    command: [yarn, start]

起動

cd docker && docker-compose up で起動。

2017-11-25 (1).png

Actionの作成

新規作成

touch src/actions/coin.jsでファイルを新規作成。

2017-11-25 (2).png

coin.jsにデフォルト値が設定され、index.jsが追加される。

src/actions/coin.js
import { createAction } from 'redux-actions'
src/actions/index.js(自動生成)
export * from "./coin";

この時点のソース

actionの追加

つぎに、coin.jsgetCoinRequestと書いて保存。

src/actions/coin.js(保存前)
import { createAction } from 'redux-actions'
getCoinRequest
src/actions/coin.js(保存後)
import { createAction } from "redux-actions";

export const getCoinRequest = createAction("GET_COIN_REQUEST");
export const getCoinSuccess = createAction("GET_COIN_SUCCESS");
export const getCoinFailure = createAction("GET_COIN_FAILURE");

Reducerの作成

新規作成

touch src/reducers/coin.jsでファイルを新規作成。

2017-11-25 (4).png

src/reducers/coin.js
import { handleActions } from 'redux-actions'
import * as actions from '../actions'

const initialState = {}

export default handleActions(
  {},
  initialState
)
src/reducers/index.js(自動生成)
import { combineReducers } from "redux";
import { routerReducer } from "react-router-redux";
import coin from "./coin";
export default combineReducers({
  coin,
  routing: routerReducer
});

actionの追加

つぎに、export default handleActions( {}, initialState){}getCoinRequestと書いて保存。

src/reducers/coin.js(保存前)
import { handleActions } from 'redux-actions'
import * as actions from '../actions'

const initialState = {}

export default handleActions(
  {getCoinRequest},
  initialState
)
src/reducers/coin.js(保存後)
import { handleActions } from "redux-actions";
import * as actions from "../actions";

const initialState = {};

export default handleActions(
  {
    [actions.getCoinRequest]: (state, action) => ({
      ...state
    }),
    [actions.getCoinSuccess]: (state, action) => ({
      ...state
    }),
    [actions.getCoinFailure]: (state, action) => ({
      ...state
    })
  },
  initialState
);

2017-11-25 (5).png

手作業

initialStateについては手動で設定。

const initialState = {
  coin: {
    id: "",
    name: "",
    symbol: "",
    rank: "",
    price_usd: "",
    price_btc: "",
    "24h_volume_usd": "",
    market_cap_usd: "",
    available_supply: "",
    total_supply: "",
    max_supply: "",
    percent_change_1h: "",
    percent_change_24h: "",
    percent_change_7d: "",
    last_updated: ""
  }
};

Sagaの作成

新規作成

同様に、touch src/sagas/coin.jsで作成。

src/sagas/coin.js
import { put, call,takeLatest } from 'redux-saga/effects';
import * as actions from '../actions';
import api from '../api';

export default [];
src/sagas/index.js(自動生成)
import { all } from "redux-saga/effects";
import coin from "./coin";
export default function* rootSaga() {
  yield all([...coin]);
}

2017-11-25 (6).png

actionの追加

同様に、getCoinRequestを書いて保存。

src/sagas/coin.js(保存前)
import { put, call,takeLatest } from 'redux-saga/effects';
import * as actions from '../actions';
import api from '../api';

getCoinRequest

export default [];
src/sagas/coin.js(保存後)
import { put, call, takeLatest } from "redux-saga/effects";
import * as actions from "../actions";
import api from "../api";

export function* handleGetCoinRequest(action) {
  try {
    const { data } = yield call(api.getCoinRequest, action.payload);
    yield put(actions.getCoinSuccess(data));
  } catch (error) {
    yield put(actions.getCoinFailure(error));
  }
}

export default [
  takeLatest(actions.getCoinRequest.toString(), handleGetCoinRequest)
];

Axiosの作成

新規作成

touch src/api/coin.js

src/api/coin.js
import axios from "../axiosConfig"
src/api/index.js(自動生成)
import * as coin from "./coin";
const api = {
  ...coin
};
export default api;

Actionの追加

src/api/coin.js(保存前)
import axios from "../axiosConfig"

getCoinRequest
src/api/coin.js(保存後)
import axios from "../axiosConfig";

export const getCoinRequest = config => axios.get(``, config);

編集

サンプルにそって以下のように書き換える。

src/api/coin.js
import axios from "../axiosConfig";

export const getCoinRequest = (id, config) => axios.get(`/v1/ticker/${id}/`, config);

コンテナの作成

新規作成

touch src/containers/CoinsShow.js

src/contaiiners/CoinsShow.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions'

class ClassNameHere extends Component{
  render () {
    return ()
  }
}

ClassNameHere.propTypes = {}

const mapStateToProps = (state, ownProps) => {
  return {}
}
const mapDispatchToProps = dispatch => bindActionCreators(
  {},
  dispatch
)

export default connect(mapStateToProps, mapDispatchToProps)(ClassNameHere)

編集

以下のようにコンテナを編集。ここではs2sは使用されない。

src/containers/CoinsShow.js
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions'
import styled from 'styled-components'
import { Link} from 'react-router'
import {Breadcrumb,ListGroup,ListGroupItem,PageHeader,Panel} from 'react-bootstrap';

class CoinsShow extends Component{
  componentDidMount(){
   this.props.getCoinRequest(this.props.params.id)
  }
  render () {
    const { coin } = this.props
    return (
      <Wrapper>
        <PageHeader>Coins Show</PageHeader>
        <Breadcrumb>
          <Breadcrumb.Item>
            <Link to='/'>Home</Link>
          </Breadcrumb.Item>
          <Breadcrumb.Item active>
            {coin.name}
          </Breadcrumb.Item>
        </Breadcrumb>
        <Panel header={coin.name}>
          <ListGroup>
            <ListGroupItem><small>id: {coin.id}</small></ListGroupItem>
            <ListGroupItem><small>name: {coin.name} </small></ListGroupItem>
            <ListGroupItem><small>symbol: {coin.symbol} </small></ListGroupItem>
            <ListGroupItem><small>rank: {coin.rank} </small></ListGroupItem>
            <ListGroupItem><small>price_usd: {coin.price_usd} </small></ListGroupItem>
            <ListGroupItem><small>price_btc: {coin.price_btc} </small></ListGroupItem>
            <ListGroupItem><small>24h_volume_usd: {coin["24h_volume_usd"]} </small></ListGroupItem>
            <ListGroupItem><small>market_cap_usd: {coin.market_cap_usd} </small></ListGroupItem>
            <ListGroupItem><small>available_supply: {coin.available_supply} </small></ListGroupItem>
            <ListGroupItem><small>total_supply: {coin.total_supply} </small></ListGroupItem>
            <ListGroupItem><small>max_supply: {coin.max_supply} </small></ListGroupItem>
            <ListGroupItem><small>percent_change_1h: {coin.percent_change_1h} </small></ListGroupItem>
            <ListGroupItem><small>percent_change_24h: {coin.percent_change_24h} </small></ListGroupItem>
            <ListGroupItem><small>percent_change_7d: {coin.percent_change_7d} </small></ListGroupItem>
            <ListGroupItem><small>last_updated: {coin.last_updated}</small></ListGroupItem>
          </ListGroup>
        </Panel>
      </Wrapper>
    )
  }
}

CoinsShow.propTypes = {}


const Wrapper = styled.div`
  margin: 25px 40px;
`

const mapStateToProps = (state, ownProps) => {
  return {
    coin: state.coin.coin
  }
}
const mapDispatchToProps = dispatch => bindActionCreators(
  {
    getCoinRequest: bindActionCreators( actions.getCoinRequest, dispatch)
  },
  dispatch
)

export default connect(mapStateToProps, mapDispatchToProps)(CoinsShow)

動作確認

開発サーバ

色々エラーでているけれど確認成功。最低限動作させるために追加したソースは後述。

2017-11-25 (9).png

ビルド

ビルドも無事成功。

cd docker && docker-compose run s2s yarn build

2017-11-25 (10).png

追加したソース

開発サーバを動作させるためにサンプルを参考に以下のソースを追加した。

src/axiosConfig.js
import axios from 'axios'

const API_ROOT = process.env.API_ROOT || 'https://api.coinmarketcap.com'

axios.defaults.timeout = 5000
axios.defaults.baseURL = API_ROOT
axios.defaults.headers.post['Content-Type'] = 'application/json'

export default axios
src/store/configureStore.js
import { createStore, compose, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { routerMiddleware } from 'react-router-redux'
import rootReducer from '../reducers'
import rootSaga from '../sagas'

export default function configureStore(history, initialState) {

  const sagaMiddleware = createSagaMiddleware()
  const store = createStore(
    rootReducer,
    initialState,
    compose(
      applyMiddleware(
        sagaMiddleware,
        routerMiddleware(history)
      )
    )
  )
  sagaMiddleware.run(rootSaga)

  return store
}
src/routes.js
import React from 'react'
import { Route, IndexRoute } from 'react-router';

import CoinsShow from './containers/CoinsShow'

export default (
  <Route path="/" component={CoinsShow} >
    <IndexRoute component={CoinsShow} />
    <Route path="coins/:id" component={CoinsShow} />
  </Route>
)
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';

import { Provider } from 'react-redux'
import { Router, browserHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'

import routes from './routes'
import configureStore from './store/configureStore'

const store = configureStore()
const history = syncHistoryWithStore(browserHistory, store)

ReactDOM.render(
  <Provider store={store}>
    <Router history={history} routes={routes} />
  </Provider>
  , document.getElementById('root'));

参考

s2s-redux-actions-sample
さよならボイラープレート。s2sによる高速reduxアプリケーション構築
yarnチートシート
コマンド一発でReactの開発環境を構築してくれるFacebook製ツール「create-react-app」
Reactを秒速で使い始められるcreate-react-appの使い方と使い心地
Reactチュートリアルをcreate-react-appの形式に合わせて焼きなおす
Yarnで react+browserify+babel+JSX変換してみる
create-react-appでyarnしたいけど対応してないのでreact-scriptsだけ使う

4
3
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
4
3