Chapter 01

React開発ノート

snaga
snaga
2022.05.08に更新

開発開始

アプリのひな型作成

npx create-react-app my-great-app
npx create-react-app my-great-app --template cra-template-pwa

追加パッケージのインストール

  • Redux
    • yarn add redux react-redux
  • React-router ページ遷移
    • yarn add react-router-dom
  • Redux-form フォーム
    • yarn add redux-form
  • REST API 実行
    • yarn add axios
  • Redux の非同期処理
    • yarn add redux-thunk
  • Material-UI
    • yarn add @mui/material @emotion/react @emotion/styled
      • yarn add @material-ui/core @material-ui/icons
  • Chart.js
    • yarn add react-chartjs-2 chart.js

開発用サーバ起動

yarn run start
set HTTPS=true
yarn run start

Redux による状態の管理

action の定義

action を定義して、action を返す dispatch 関数を作る。

// actions/index.js
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

export const increment = () => ({
  type: INCREMENT
});
export const decrement = () => ({
  type: DECREMENT
});

reducer の定義

reducer を定義する。

// reducers/count.js
import { INCREMENT, DECREMENT } from '../actions';

const initialState = { value: 0 };

export default (state = initialState, action) => {
  switch (action.type) {
    case INCREMENT:
      return { value: state.value + 1};
    case DECREMENT:
      return { value: state.value - 1};
  }
  return state;
}

複数の reducer を統合する。

// reducers/index.js
import { combineReducers } from 'redux';
import count from './count';

export default combineReducers({ count });
//export default combineReducers({ foo, bar, baz });

store の定義

全てのコンポーネントから state を参照できるようにする。

// index.js
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducers';

// アプリ内で唯一のstore
const store = createStore(reducer);

// AppコンポーネントをProviderでwrapする
ReactDOM.render(
  <Provider store={store}>
    <App/>
  </Provider>,
  ...
);

state と dispatch 関数をコンポーネントの props に関連付けする

connect 関数で state と dispatch 関数をコンポーネントの props に関連付けする。

// App.js
import { connect } from 'react-redux';
import { increment, decrement } from './actions';

class App extends Component {
  // 描画
  render() {
    const props = this.props;
    return (
      <React.Fragment>
        <div>value: { props.value }</div>
        <button onClick={props.increment}>+1</button>
      </React.Fragment>
    );
  }
}

// stateをpropsに関連付けする
const mapStateToProps = state => ({ value: state.count.value });

// actionのdispatch関数をpropsに関連付けする
const mapDispatchToProps = dispatch => ({
  increment: () => dispatch(increment()),
  decrement: () => dispatch(decrement())
});

// short hand
//const mapDispatchToProps = ({ increment, decrement });

export default connect(mapStateToProps, mapDispatchToProps)(App);

共通の項目を持つ連想配列の配列を、連想配列に変換する

import _ from 'lodash';

// [{'id': 1, ... }, {'id': 2, ...}]
//  -> { 1: {'id': 1, ... }, 2: {'id': 2, ...}}
_.mapKeys(vals, 'id');

REST API の実行

redux-thunk ミドルウェアの適用。

// index.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';

const store = createStore(reducer, applyMiddleware(thunk));

アクションの実装。

// actions/index.js
import axios from 'axios';

export const readEvents = () => async dispatch => {
  const response = await axios.get(`https://foo.com/bar?querystring`);
    dispatch({ type: READ_EVENTS, response });

}

reducer の実装。

// reducers/event.js
import { READ_EVENTS } from '../actions';

export default (state = {}, action) => {
  switch (action.type) {
    case READ_EVENTS:
      return action.response.data;
  }
  return state;
}

コンポーネントで props にマップして参照。

// components/event_index.js

class EventIndex extends Components {
  return (<div>{console.log(this.props.events)}</div>);
}

const mapStateToProps = state => ({ events: state.events });

レイアウト

画面の高さを 100%使う。

html,
body,
#root {
  height: 100%;
}