Help us understand the problem. What is going on with this article?

個人的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周りベストプラクティス⑦ - 運用編

enshi
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした