#前の記事
前回:個人的React周りベストプラクティス⑤ - Reduxディレクトリ構成編
一番始め:個人的React周りベストプラクティス① - 構成編
#フロントサイド
##新規PJ作成
### create-react-appで基本的なReactアプリを作成する
$ create-react-app redux_hoge
redux_hogeというディレクトリの中に色々と作られる
### 必要パッケージ、ミドルウェアインストール
参考:個人的React周りベストプラクティス③ - Reduxパッケージ・ミドルウェア編
$ cd redux_hoge
$ npm install redux react-redux
$ npm install axios redux-actions material-ui react-tap-event-plugin react-router-dom redux-promise redux-logger
### デフォルトで作られたファイルはいらないので、src内の全ファイル削除
$ rm -f src/*
### 空のディレクトリを作成する
参考:個人的React周りベストプラクティス⑤ - Reduxディレクトリ構成編
$ mkdir -p src/actions src/components/App src/constants src/css src/lib src/reducers
### index.jsを作成する
import React from 'react';
import { render } from 'react-dom';
import { applyMiddleware, createStore } from 'redux';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import './css/reset.css'; //reset.css
import './css/common.css'; //common.css
import Allreducer from './reducers'; //reducerをcombineReducersで合体しているファイル
import App from './components/App/App'; //ルートコンポーネント
//Middleware
import promiseMiddleware from 'redux-promise'; //非同期処理用
import { createLogger } from 'redux-logger'; //ログ取得用
//Material-Ui
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import injectTapEventPlugin from "react-tap-event-plugin";
injectTapEventPlugin(); //タッチイベント実行用
//reducerとミドルウェアを指定してstore作成
let store = createStore(
Allreducer,
applyMiddleware(promiseMiddleware, createLogger())
);
render(
<MuiThemeProvider>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</MuiThemeProvider>,
document.getElementById('root')
)
### common.cssとreset.cssを作成する
$ touch src/css/common.css
現場で決められている場合はそれを
決められていない場合は、ネットから拾ってきたものをコピペ
### reducerを統括するファイルを作成する
import { combineReducers } from 'redux'
const reducer = combineReducers({
});
export default reducer;
##ページ追加例
コードで理解するRedux(React使用)
例として上記で作った
数値を入力してボタンを押すと税込みの金額が出るページを作ります。
### action作成
ファイルが異なっていてもtypeが同じcreateActionが複数存在すると両方のreducerが走ってしまうので、
「ADD_TAX_」のようにプレフィックスとしてドメインを付けるべきだと思う。(良い方法があれば教えてください。。)
import { createAction } from 'redux-actions';
//金額変更
export let changeMoney = createAction('ADD_TAX_CHANGE_MONEY');
//税込み計算
export let calc = createAction('ADD_TAX_CALC');
### reducer作成
import { handleActions } from 'redux-actions';
import * as AddTax from '../actions/addTax';
const initialState = {
money: '',
includeTax: '',
};
const Reducer = handleActions({
//金額変更
[AddTax.changeMoney]: (state, action) => (
Object.assign({}, state, {
money: action.payload,
})
),
//税込み計算
[AddTax.calc]: (state, action) => (
Object.assign({}, state, {
includeTax: parseInt(action.payload * 1.08, 10),
})
),
}, initialState);
export default Reducer;
reducer/index.jsで追加したreducerを読み込む
import { combineReducers } from 'redux'
import AddTax from './addTax' ← 追加
const reducer = combineReducers({
AddTax, ← 追加
});
export default reducer;
### component追加
Container Component
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Component from './AddTax';
import * as Actions from './../../actions/addTax';
const mapStateToProps = (state) => (state.AddTax);
const mapDispatchToProps = (dispatch) => (bindActionCreators(Actions, dispatch));
const Container = connect(
mapStateToProps,
mapDispatchToProps,
)(Component);
export default Container;
Presentaional Component
import React from 'react';
//Material-Ui
import TextField from 'material-ui/TextField';
import RaisedButton from 'material-ui/RaisedButton';
export default class Component extends React.Component {
//金額変更
changeMoney(event, value){
this.props.changeMoney(value);
}
//税込み計算
calc(){
this.props.calc(this.props.money);
}
render() {
return (
<div>
<TextField
id="money"
value={this.props.money}
onChange={this.changeMoney.bind(this)}
/>
<RaisedButton
label="計算"
onTouchTap={this.calc.bind(this)}
/>
{this.props.includeTax}
</div>
);
}
};
### ルーティングファイル追加
<NavLink>コンポーネントはtoで指定したものと現在のURLパスが一致した時に、activeClassNameで指定したものがclassに追加される。
現在開いているページのボタンの色を変えたいときなどに使用する。
import React from 'react';
import { NavLink, Route } from 'react-router-dom'
//Material-Ui
import Paper from 'material-ui/Paper';
import * as Colors from 'material-ui/styles/colors';
import AddTax from '../AddTax/AddTaxContainer'
export default class Component extends React.Component {
render() {
return (
<div>
<Paper
style={{
backgroundColor: Colors.cyan400,
padding: "2px",
}}
>
<NavLink to='/addtax' className="menuButton" activeClassName="activeMenuButton">
消費税計算
</NavLink>
</Paper>
<Route exact path='/' component={AddTax} /> {/* /のときAddTaxを読み込む */}
<Route path='/addtax' component={AddTax} /> {/* /addtaxのときAddTaxを読み込む */}
</div>
);
}
};
### common.cssにグローバルメニューのボタンのスタイルを追記する
.menuButton {
color: black;
font-size: 14px;
text-decoration: none;
background-color: paleturquoise;
margin: 5px;
padding: 12px;
border-radius: 2px;
display: inline-block;
vertical-align: middle;
}
.menuButton:hover {
background-color: lightgoldenrodyellow; /* ボタンをホバーした時背景色を変更する */
}
.activeMenuButton {
background-color: lightgoldenrodyellow; /* アクティブのときボタンの背景色を変更する */
}
#サーバーサイド
##新規PJ作成
### nodejsのテンプレートでプロジェクト作成
$ mkdir lambda_hoge
$ cd lambda_hoge
$ sls create -t aws-nodejs
$ ls
handler.js serverless.yml
$ rm handler.js
作られるファイルの説明
ファイル | 説明 |
---|---|
handler.js | lambda関数のサンプル(消してもOK) |
serverless.yml | デプロイの設定ファイル |
### 必要パッケージインストール
参考:個人的React周りベストプラクティス④ - Lamdaパッケージ編
$ npm init
出て来る質問はすべて何も入力せずEnter
$ npm install serverless-offline
$ npm install lambda-wrapper ← 不要なら入れなくても良い
$ npm install serverless-plugin-split-stacks ← 不要なら入れなくても良い
### serverless.yml書き換え
インデントはタブではなくスペースでなければならない
service: lambda_hoge
package:
include:
- ../lib/**
- ../commonConstant.js
plugins:
- serverless-offline
- serverless-plugin-split-stacks ← 不要なら入れなくても良い
custom:
serverless-offline:
host: 0.0.0.0
port: 50000
provider:
name: aws
runtime: nodejs6.10
region: ap-northeast-1
memorySize: 128
timeout: 30
functions:
それぞれの内容解説
項目 | 説明 |
---|---|
service | アプリケーション名 |
package | パッケージに含めるもの、含めないものを設定する |
include | serverless.ymlよりも上位の階層にあるファイルを使えるようにする デプロイした時にserverless.ymlと同階層になるのでrequire時に注意(process.env.IS_OFFLINE === 'true'で判定しローカルの時とデプロイした時で分ける) |
plugins | 追加パッケージ |
custom | カスタム設定 |
serverless-offline | serverless-offlineのカスタム設定 |
host | serverless-offlineのホスト設定 |
port | 関数をローカルで実行した時のポート、開いているポートをPJのメンバーで合わせる デフォルトだと3000になってしまいフロント側とかぶる |
provider | アプリケーションのデプロイ先の設定 |
name | デプロイ先。”aws”固定 |
runtime | 言語 |
region | リージョン 東京の場合はap-northeast-1 |
memorySize | デフォルトのメモリサイズ |
timeout | デフォルトのタイムアウト時間 |
functions | 関数の設定 |
##関数追加
### 関数作成
'use strict';
module.exports.handler = (event, context, callback) => {
let response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token",
},
body: 'lambdaからお送りします',
};
callback(null, response);
}
### serverless.yml追記
serverless.ymlの一番下に以下を追記する
functions:
testFunction: ← lambdaに登録される関数名
handler: testFunction.handler ← 実行する関数(testFunction.jsのhandlerという関数)
events:
- http: post testFunction ← APIGatewayの実行パス(https://~~~~~~~~/testFunctionにPOSTでアクセスすると実行される)