Edited at

個人的React周りベストプラクティス⑥ - 開発編

More than 1 year has passed since last update.


前の記事

前回:個人的React周りベストプラクティス⑤ - Reduxディレクトリ構成編

一番始め:個人的React周りベストプラクティス① - 構成編


フロントサイド


新規PJ作成


:one: create-react-appで基本的なReactアプリを作成する

$ create-react-app redux_hoge

redux_hogeというディレクトリの中に色々と作られる


:two: 必要パッケージ、ミドルウェアインストール

参考:個人的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


:three: デフォルトで作られたファイルはいらないので、src内の全ファイル削除

$ rm -f src/*


:four: 空のディレクトリを作成する

参考:個人的React周りベストプラクティス⑤ - Reduxディレクトリ構成編

$ mkdir -p src/actions src/components/App src/constants src/css src/lib src/reducers


:five: index.jsを作成する


src/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')
)



:six: common.cssとreset.cssを作成する

$ touch src/css/common.css


src/css/reset.css

現場で決められている場合はそれを

決められていない場合は、ネットから拾ってきたものをコピペ


:seven: reducerを統括するファイルを作成する


src/reducers/index.js

import { combineReducers } from 'redux'

const reducer = combineReducers({
});

export default reducer;



ページ追加例

コードで理解するRedux(React使用)

例として上記で作った

数値を入力してボタンを押すと税込みの金額が出るページを作ります。


:one: action作成

ファイルが異なっていてもtypeが同じcreateActionが複数存在すると両方のreducerが走ってしまうので、

「ADD_TAX_」のようにプレフィックスとしてドメインを付けるべきだと思う。(良い方法があれば教えてください。。)


src/actions/addTax.js

import { createAction } from 'redux-actions';

//金額変更
export let changeMoney = createAction('ADD_TAX_CHANGE_MONEY');

//税込み計算
export let calc = createAction('ADD_TAX_CALC');



:two: reducer作成


src/reducers/addTax.js

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を読み込む


src/reducers/index.js

import { combineReducers } from 'redux'

import AddTax from './addTax' ← 追加

const reducer = combineReducers({
AddTax, ← 追加
});

export default reducer;



:four: component追加

Container Component


src/components/AddTax/AddtaxContainer.js

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


src/components/AddTax/AddTax.js

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>
);
}
};



:five: ルーティングファイル追加

<NavLink>コンポーネントはtoで指定したものと現在のURLパスが一致した時に、activeClassNameで指定したものがclassに追加される。

現在開いているページのボタンの色を変えたいときなどに使用する。


src/components/App/App.js

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>
);
}
};



:six: common.cssにグローバルメニューのボタンのスタイルを追記する


src/css/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作成


:one: 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
デプロイの設定ファイル


:two: 必要パッケージインストール

参考:個人的React周りベストプラクティス④ - Lamdaパッケージ編

$ npm init

出て来る質問はすべて何も入力せずEnter

$ npm install serverless-offline
$ npm install lambda-wrapper ← 不要なら入れなくても良い
$ npm install serverless-plugin-split-stacks ← 不要なら入れなくても良い


:three: serverless.yml書き換え

インデントはタブではなくスペースでなければならない


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
関数の設定


関数追加


:one: 関数作成


testFunction.js

'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);
}



:two: serverless.yml追記

serverless.ymlの一番下に以下を追記する


serverless.yml

functions:

testFunction: ← lambdaに登録される関数名
handler: testFunction.handler ← 実行する関数(testFunction.jsのhandlerという関数)
events:
- http: post testFunction ← APIGatewayの実行パス(https://~~~~~~~~/testFunctionにPOSTでアクセスすると実行される)


次の記事

個人的React周りベストプラクティス⑦ - 運用編