目次
プロジェクトの作成
コマンドでプロジェクトを作成しましょう。
ディレクトリを移動して、以下を実行してプロジェクトを作成します。
create-react-app calculator
「Success!」が表示されたらプロジェクトができていると思うので、
コマンド操作で作成したプロジェクトへ移動しましょう。
cd !$
パッケージを使用するためのコマンドをたたきます。
※ない場合はnpmにてインストールしてください。
yarn add redux react-redux prop-types
ファイル構成
src以下のようなファイル構成になります。
src
├── actions
└── index.js
├── components
└── CancelBtn.js
└── EqualBtn.js
└── NumBtn.js
└── PlusBtn.js
├── containers
└── CalculatorContainer.js
├── reducers
├── index.js
└── calculator.js
├── utils
└── actionType.js
├── index.js
└── serviceWorker.js
こちらのファイル構成をコマンドで作成していきます。
cd src
mkdir actions components containers reducers utils
touch actions/index.js
cd components
touch CancelBtn.js EqualBtn.js NumBtn.js PlusBtn.js
cd ../
touch containers/CalculatorContainer.js
touch reducers/calculator.js reducers/index.js
touch utils/actionTypes.js
cd ../
ファイル編集
ディレクトリsrc以下のファイルを編集していきます。
utils/actionTypes.js
export const INPUT_NUM = 'INPUT_NUM';
export const PLUS = 'PLUS';
export const CANCEL = 'CANCEL';
export const EQUAL = 'EQUAL';
reducers/index.js
import { combineReducers } from 'redux';
import calculator from './calcutor';
const reducer = combineReducers({
calculator,
});
export default reducer;
reducers/calculator.js
import {
INPUT_NUM,
PLUS,
CANCEL,
EQUAL,
} from "../utils/actionTypes";
const initialAppState = {
inputValue: 0,
resultValue: 0,
formula: '',
};
/**
* プラスボタンが押下時の計算式確認
* @param text 計算式
* @param sym 記号(シンボル)
* @returns {string|計算式}
*/
const plusFormulaCheck = (text, sym) => {
// 末尾の文字を取得
var tmpText = text.slice(-1);
if ((tmpText === '+') && (sym === '=')) {
// 末尾の「+」を削除して「=」にする
return text.slice(0, -1) + sym;
} else if (tmpText === '+') {
// 末尾が「+」ならそのまま返す
return text;
} else {
// 計算式に「+」をいれる
return text + sym;
}
}
/**
* イコールボタンが押下時の計算式確認
* @param text 計算式
* @param sym 記号(シンボル)
* @param resultValue 計算結果
* @param answer 計算結果
* @returns {string|計算式}
*/
const equalFormulaCheck = (text, sym, resultValue, answer) => {
var tmpText = text.slice(-1);
if (tmpText === '+') {
// 末尾の「+」を削除
return text.slice(0, -1);
}
// 計算結果の文字数
var lenResult = resultValue.toString().length;
// 最後の記号の取得
tmpText = text.slice(text.length - (lenResult + 1), text.length - (lenResult));
if (tmpText === '=') {
// 最後の記号が「=」なら何もせずに返却
return text;
} else {
// 「=」と計算結果を繋げて返却
return text + sym + answer;
}
}
/**
* 計算式の確認
* @param text 計算式
* @param resultValue 計算結果
* @returns {string|計算式}
*/
const formulaCheck = (text, resultValue) => {
// 計算結果の文字数
var lenResult = resultValue.toString().length;
// 最後の記号を取得
var tmpText = text.slice(text.length - (lenResult + 1), text.length - (lenResult));
if (tmpText === '=') {
// 「+」をつけて返却(今回は足し算だけの想定なので)
return text + '+';
} else {
return text;
}
}
/**
* ボタン押下時のアクション
* @param state
* @param action
* @returns state
*/
const calculator = (state = initialAppState, action) => {
if (action.type === INPUT_NUM) {
return {
...state,
inputValue: state.inputValue * 10 + action.number,
formula: formulaCheck(state.formula, state.resultValue) + action.number,
};
} else if (action.type === PLUS) {
return {
...state,
inputValue: 0,
resultValue: state.resultValue + state.inputValue,
formula: plusFormulaCheck(state.formula, '+'),
};
} else if (action.type === EQUAL) {
return {
...state,
inputValue: 0,
resultValue: state.resultValue + state.inputValue,
formula: equalFormulaCheck(
state.formula,
'=',
state.resultValue,
state.resultValue + state.inputValue
),
};
} else if (action.type === CANCEL) {
return {
...state,
inputValue: 0,
resultValue: 0,
formula: '',
};
} else {
return state;
}
};
components/CancelBtn.js
import React from 'react';
import PropTypes from 'prop-types';
const CancelBtn = ({ onClick }) => (
<button onClick={onClick}>C</button>
);
CancelBtn.propTypes = {
onClick: PropTypes.func.isRequired,
};
export default CancelBtn;
components/EqualBtn.js
import React from 'react';
import PropTypes from 'prop-types';
const EqualBtn = ({ onClick }) => (
<button onClick={onClick}>=</button>
);
EqualBtn.propTyes = {
onClick: PropTypes.func.isRequired,
};
export default EqualBtn;
components/NumBtn.js
import React from 'react';
import PropTypes from 'prop-types';
const NumBtn = ({ num, onClick }) => (
<button onClick={onClick}>{num}</button>
);
NumBtn.PropsTypes = {
onClick: PropTypes.func.isRequired,
};
export default NumBtn;
components/PlusBtn.js
import React from 'react';
import PropTypes from 'prop-types';
const PlusBtn = ({ onClick }) => (
<button onClick={onClick}>+</button>
);
PlusBtn.propTypes = {
onClick: PropTypes.func.isRequired,
};
export default PlusBtn;
actions/index.js
import {
INPUT_NUM,
PLUS,
CANCEL,
EQUAL,
} from "../utils/actionTypes";
export const onNumClick = (number) => ({
type: INPUT_NUM,
number,
});
export const onPlusClick = () => ({
type: PLUS,
});
export const onCancelClick = () => ({
type: CANCEL,
});
export const onEqualClick = () => ({
type: EQUAL,
});
containers/CalculatorContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import NumBtn from "../components/NumBtn";
import PlusBtn from "../components/PlusBtn";
import CancelBtn from "../components/CancelBtn";
import EqualBtn from "../components/EqualBtn";
import * as actoins from "../actions";
class CalculatorContainer extends Component {
render() {
const { calcultor, actions } = this.props;
return(
<div>
<div>
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
</div>
<div>
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
</div>
<div>
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
<NumBtn num={} onClick={() => actions.onNumClick()} />
</div>
<div>
<NumBtn num={} onClick={() => actions.onNumClick()} />
<PlusBtn onClick={actions.onPlusClick} />
<EqualBtn onClick={actions.onEqualClick} />
</div>
<div>
<CancelBtn onClick={actions.onCancelClick} />
</div>
<div>
<div>計算式:{calculator.formula}</div>
<div>計算結果:<span>{calculator.resultValue}</span></div>
</div>
</div>
);
}
}
const mapState = (state, ownProps) => ({
calculator : state.calculator,
});
function mapDispatch(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapState, mapDispatch)(CalculatorContainer);
index.js
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import * as serviceWorker from './serviceWorker';
import CalculatorContainer from './containers/CalculatorContainer';
import reducer from './reducers';
const store = createStore(reducer);
render(
<Provider store={store}>
<CalculatorContainer />
</Provider>,
document.getElementById('root')
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
アプリを実行
コマンドでアプリを実行しましょう。
yarn start
ボタンを押して、計算してみましょう。
無事に計算式と計算結果がでたら成功です。
※計算式の簡単な制御は入れてありますが、複雑な操作をするとおかしくなります。
以上、React + Reduxでの電卓アプリ作成でした。
コメント