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設定
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
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
で起動。
Actionの作成
新規作成
touch src/actions/coin.js
でファイルを新規作成。
coin.jsにデフォルト値が設定され、index.jsが追加される。
import { createAction } from 'redux-actions'
export * from "./coin";
actionの追加
つぎに、coin.js
にgetCoinRequest
と書いて保存。
import { createAction } from 'redux-actions'
getCoinRequest
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
でファイルを新規作成。
import { handleActions } from 'redux-actions'
import * as actions from '../actions'
const initialState = {}
export default handleActions(
{},
initialState
)
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
と書いて保存。
import { handleActions } from 'redux-actions'
import * as actions from '../actions'
const initialState = {}
export default handleActions(
{getCoinRequest},
initialState
)
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
);
手作業
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
で作成。
import { put, call,takeLatest } from 'redux-saga/effects';
import * as actions from '../actions';
import api from '../api';
export default [];
import { all } from "redux-saga/effects";
import coin from "./coin";
export default function* rootSaga() {
yield all([...coin]);
}
actionの追加
同様に、getCoinRequest
を書いて保存。
import { put, call,takeLatest } from 'redux-saga/effects';
import * as actions from '../actions';
import api from '../api';
getCoinRequest
export default [];
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
import axios from "../axiosConfig"
import * as coin from "./coin";
const api = {
...coin
};
export default api;
Actionの追加
import axios from "../axiosConfig"
getCoinRequest
import axios from "../axiosConfig";
export const getCoinRequest = config => axios.get(``, config);
編集
サンプルにそって以下のように書き換える。
import axios from "../axiosConfig";
export const getCoinRequest = (id, config) => axios.get(`/v1/ticker/${id}/`, config);
コンテナの作成
新規作成
touch 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'
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は使用されない。
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)
動作確認
開発サーバ
色々エラーでているけれど確認成功。最低限動作させるために追加したソースは後述。
ビルド
ビルドも無事成功。
cd docker && docker-compose run s2s yarn build
追加したソース
開発サーバを動作させるためにサンプルを参考に以下のソースを追加した。
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
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
}
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>
)
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だけ使う