このチャプターの目次
開発開始
アプリのひな型作成
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%;
}