React+Redux クラスコンポーネント・関数コンポーネントおよびHooksの書き換えサンプル
クラスコンポーネントから乗り換えてみたいので勉強中。
webpack 4.39.2react 16.13.1react-dom 16.13.1redux 4.0.5react-redux 7.2.0
もくじ
ここでは↓こういうのを作る。
めんどくさい人向け
GitHubに上げました。
napoporitataso/react-redux-hooks-example-20200517
git clone https://github.com/napoporitataso/react-redux-hooks-example-20200517.git
cd react-redux-hooks-example-20200517
npm install
npx webpack-dev-server
以下は手動で作成して動かす方法。
必要なものインストール
ここでは react-redux-hooks-example-20200517
ディレクトリ以下でやる。
mkdir react-redux-hooks-example-20200517
cd react-redux-hooks-example-20200517
npm init -y
必要なものをインストール。
npm install react react-dom redux react-redux
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader webpack webpack-cli webpack-dev-server
Webpack設定
プロジェクト直下に webpack.config.js
を作成する。Babel と webpack-dev-server の設定だけ。
const path = require('path');
module.exports = {
mode: 'development',
entry: './src/index.jsx',
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['@babel/preset-env', '@babel/preset-react']
}
}
],
},
resolve: {
extensions: ['.js', '.jsx']
},
devServer: {
contentBase: path.join(__dirname, 'dist'),
host: 'localhost',
port: 3000,
open: true,
},
};
ソースコード (周辺)
ざっと以下ファイルを用意する。本題にあまり関係無いけど必要。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<meta name="landscape" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<meta name="format-detection" content="telephone=no" />
<title>サンプル</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript" src="./bundle.js"></script>
</body>
</html>
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from './reducers';
import Counter_class from './components/Counter_class';
import Counter_func from './components/Counter_func';
import Counter_hooks from './components/Counter_hooks';
const store = createStore(reducer());
ReactDOM.render(
<Provider store={store}>
<Counter_class />
<Counter_func />
<Counter_hooks />
</Provider>,
document.getElementById('app')
);
export const COUNT_UP = 'COUNT_UP';
export const COUNT_DOWN = 'COUNT_DOWN';
export const countUp = (value) => {
return {
type: COUNT_UP,
value
};
}
export const countDown = (value) => {
return {
type: COUNT_DOWN,
value
};
}
import { combineReducers } from 'redux';
import counter from './counter';
const reducer = () => combineReducers({
counter,
});
export default reducer;
import { COUNT_UP, COUNT_DOWN } from '../actions';
const initialState = {
clickCount: 0,
currentValue: 0,
};
export default (state = initialState, action) => {
switch (action.type) {
case COUNT_UP:
return {
clickCount: state.clickCount + 1,
currentValue: state.currentValue + action.value
};
case COUNT_DOWN:
return {
clickCount: state.clickCount + 1,
currentValue: state.currentValue - action.value
};
default:
return state;
}
}
ソースコード (コンポーネント)
本題。以下の3つのコンポーネントは同じように動作する。
クラスコンポーネント
普通のクラスコンポーネント + mapStateToProps()
+ mapDispatchToProps()
。
import React from 'react';
import { connect } from 'react-redux';
import { countUp, countDown } from '../actions'
class Counter extends React.Component {
handlePlusButton() {
this.props.countUp(1);
}
handleMinusButton() {
this.props.countDown(1);
}
render() {
const { currentValue, clickCount } = this.props;
return (
<div>
<h2>クラスコンポーネント</h2>
<div>clickCount: {clickCount}</div>
<div>
<button onClick={(e) => { this.handleMinusButton(e); }}>-</button>
<input type="text" value={currentValue} readOnly />
<button onClick={(e) => { this.handlePlusButton(e); }}>+</button>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
clickCount: state.counter.clickCount,
currentValue: state.counter.currentValue,
};
};
const mapDispatchToProps = (dispatch) => {
return {
countUp: function (value) {
return dispatch(countUp(value));
},
countDown: function (value) {
return dispatch(countDown(value));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
関数コンポーネント
単純にクラスコンポーネントを関数コンポーネントの書き方に変えただけ。引数で props
を受け取る。
import React from 'react';
import { connect } from 'react-redux';
import { countUp, countDown } from '../actions'
const Counter = (props) => {
const { currentValue, clickCount } = props;
const handlePlusButton = () => {
props.countUp(1);
};
const handleMinusButton = () => {
props.countDown(1);
};
return (
<div>
<h2>関数コンポーネント</h2>
<div>clickCount: {clickCount}</div>
<div>
<button onClick={(e) => { handleMinusButton(e); }}>-</button>
<input type="text" value={currentValue} readOnly />
<button onClick={(e) => { handlePlusButton(e); }}>+</button>
</div>
</div>
);
};
const mapStateToProps = (state) => {
return {
clickCount: state.counter.clickCount,
currentValue: state.counter.currentValue,
};
};
const mapDispatchToProps = (dispatch) => {
return {
countUp: function (value) {
return dispatch(countUp(value));
},
countDown: function (value) {
return dispatch(countDown(value));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
関数コンポーネント + Hooks
関数コンポーネントではHooksを使ってすっきり書ける。
useSelector()
と useDispatch()
は関数内にしか書けない。また、ここではSelectorの書き方はHooksを使わない場合の mapStateToProps()
に寄せてみた。
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { countUp, countDown } from '../actions';
const Counter = () => {
const { currentValue, clickCount } = useSelector(selector);
const dispatch = useDispatch();
const handlePlusButton = () => {
dispatch(countUp(1));
};
const handleMinusButton = () => {
dispatch(countDown(1));
};
return (
<div>
<h2>関数コンポーネント + Hooks</h2>
<div>clickCount: {clickCount}</div>
<div>
<button onClick={(e) => { handleMinusButton(e); }}>-</button>
<input type="text" value={currentValue} readOnly />
<button onClick={(e) => { handlePlusButton(e); }}>+</button>
</div>
</div>
);
};
const selector = state => {
return {
clickCount: state.counter.clickCount,
currentValue: state.counter.currentValue,
};
};
export default Counter;
動かしてみる
プロジェクト直下で以下コマンド。
npx webpack-dev-server
以上。