前言:
之前的例子都是寫的計數器,加一減一的功能,我們大致弄懂了redux分層和store數據管理,下面我們將結合現有知識寫一個終極版的計算器。以此鞏固所學知識
知識點:redux分層,react-router,一些算法及數據結構知識[棧 + 中綴轉後綴]
首段祭出源碼地址:
github:https://github.com/EmilyYoung71415/LearnReactDemo
效果圖
項目結構
├──redux-demo/ * 計算器Demo
|
|————src/ * 主程序
│
├─Components * 所有組件
│ ├─Calculator * 計算器
│ ├─Counter * 計數器
│ └─StudyDemos * 學習的一些有幫助的demo
│ └─備份文件夾 * 筆記 等我寫完博客就清
├─Error * 錯誤組件
├─Redux * Redux
│ ├─Action
│ ├─Containers
│ ├─Reducer
│ └─Store
├─Router * 路由
└─Style * 所有樣式變量
爲了便於初學者如我上手,我沒有將分層的各個功能部件寫在不同的文件下,而是採用了先寫在一起然後分成多個文件的策略。如下是我的計算器代碼,並沒有加路由。
思路是:
1.佈局:按鈕值是數組,循環數組值生成按鈕,一個函數監聽所有的點擊事件。flex佈局,固定每行幾個塊。
2.確定當前實例中擁有的所有數據,接下來在這些數據中找出應該是state的數據。
即:在當前實例[計算器]中的所有數據,確定哪些是本組件內部管理的無需存到store上的數據。
也就是確定props數據與state數據。
props數據與state數據劃分遵循三原則:
1. 是否是通過父級props傳來的,如果是則可能不是state
2. 會隨時間推移而不變嗎? 如果是則可能不是state
3. 你能根據組件中其他任何的state或者props計算出他嗎?如果能,則可能不是state
由上推理:
- 等號及等號前的數據: 通過用戶的輸入而來 會隨時間推移而變 state
- 等號後的數據: 能夠計算得出
- 按鈕的值: 來源於父級props層層傳遞而來
綜上我們可以得到屬於本組件state狀態的數據是:等號及等號前的數據
而每次計算的結果我們則存到redux的store裏。即結果來源於props。
關於結果:
我們對應一個方法和一個參數
- 方法:等於符號的點擊 equalClick => 負責向外分發action
- 參數: revdata 結果值 =>繼承自strore,this.props.revdata
難點解說: http://www.xiexianbo.xin/wordpress/?p=519
源碼解讀:
import React, {Component} from 'react';
import { createStore } from 'redux'
import { connect } from 'react-redux'
import suffixExpression from './stack'
import '../../Style/calcuator.css'
const KEYVALUE = [
{value: '7'},
{value: '8'},
{value: '9'},
{value: '←'},
{value: 'C'},
{value: '4'},
{value: '5'},
{value: '6'},
{value: '*'},
{value: '/'},
{value: '1'},
{value: '2'},
{value: '3'},
{value: '+'},
{value: '-'},
{value: '0'},
{value: '00'},
{value: '.'},
{value: '%'},
{value: '='},
{value: '('},
{value: ')'}
];
class MyCalculator extends Component {
constructor(props){
super(props);
this.state = {
valueText: '0' //實時更新用戶輸入的值
}
}
handleValueInput(data) {
let oldState = this.state.valueText;
//傳入當前文本框的值和當前按鈕按下的值,調用checkClickType依據不同的按鈕值做不同的反應,返回新的值。
let rev = this.checkClickType(oldState,data);
let newState = {};
newState.valueText = rev;
this.setState( newState)
}
checkClickType(oldvalue,value){
switch (value) {
case '=':
let resultbefore = oldvalue + ' =' ;
//向外分發action
this.props.equalClick(oldvalue);
return resultbefore;
case '←':
//刪除最後一位
oldvalue = oldvalue.substring(0,oldvalue.length-1)
return oldvalue;
case 'C':
oldvalue = '0';
return oldvalue;
case '+':
case '-':
case '/':
case '*':
case '(':
case ')':
return oldvalue + ' ' +value + ' ';//運算符與操作數以空格爲分割
default://一般數字
if(oldvalue === '0'){//清零
oldvalue = ''
}
return oldvalue + value
}
}
render() {
const {revdata} = this.props;//獲得最新的結果值
let buttonlist = [];
KEYVALUE.forEach(data => {
buttonlist.push(
<button className='div_class_button'
key={data.value}
onClick = {this.handleValueInput.bind(this,data.value)}
>{data.value}</button>
);
});
//取當前input框字符串的最後一個字符 如果是等於符號則 運算過程+結果
let str = this.state.valueText;
let laststr = str.charAt(str.length - 1)
let curValue = str;
if(laststr === '='){
curValue = str +' '+revdata;
}
return (
<div className='div_class_calculator'>
<div className='div_class_showdatabar'>
<h1>簡易計算器</h1>
<input type="text"
value={curValue}
readOnly
/>
</div>
<div className='div_class_buttonlist'>
{buttonlist}
</div>
</div>
);
}
}
/**
* @func 模塊--container
* @desc 定義映射
*/
//將UI組件的props與redux的state映射
function mapStateToProps(state) {
return {
revdata: state.revdata
}
}
//將UI組件的props與redux的action映射
function mapDispatchToProps(dispatch) {
return {
//用戶的onIncreaseClick方法與action映射([3]定義action),通過dispatch觸發reducer
equalClick: (value) => dispatch(getResult(value))
}
}
/**
* @func 模塊--action
* @desc
*/
const EQUEALBTN = 'EQUEALBTN'; //常規按鈕
const ActionGenerator = (type, num) => (num) => {
let action = { type, num : num }
return action
}
const getResult = ActionGenerator(EQUEALBTN, null);
/**
* @func 模塊--connect
*/
const App = connect(
mapStateToProps,
mapDispatchToProps
)(MyCalculator)
/**
* @func 模塊--reducer
* @desc 根據action 返回新的state
*/
function getRev(state = { revdata: 0 }, action) {
//action.num即是等號前面的字符串
switch (action.type) {
case EQUEALBTN:
//let test = '1 + 78 + 22 + ( 10 - 2 ) * 6';
let rev = suffixExpression(action.num)//具體的計算處理,我採用的是中綴轉後綴計算方法。
return { revdata: rev }
default:
return state
}
}
/**
* @func 模塊--store
* @desc 以reducer生成store對象
*/
const store = createStore(getRev)
export {
store,
App
};
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import {store,App} from './Components/Calculator/calculatorAll';
ReactDOM.render(
<Provider store={store}>
{route}
</Provider>,
document.getElementById('root')
);