1. 程式人生 > >[一步一步學react系列] 04—計算器Demo

[一步一步學react系列] 04—計算器Demo

前言:
之前的例子都是寫的計數器,加一減一的功能,我們大致弄懂了redux分層和store資料管理,下面我們將結合現有知識寫一個終極版的計算器。以此鞏固所學知識
知識點:redux分層,react-router,一些演算法及資料結構知識[棧 + 中綴轉字尾]

效果圖

這裡寫圖片描述

專案結構

├──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

原始碼解讀:

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')
);