前言:
之前的例子都是写的计数器,加一减一的功能,我们大致弄懂了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状态的数据是:等号及等号前的数据
而每次计算的结果我们则存到redux的store里。即结果来源于props。
关于结果:
我们对应一个方法和一个参数
难点解说: 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(
);
});
//取当前input框字符串的最后一个字符 如果是等于符号则 运算过程+结果
let str = this.state.valueText;
let laststr = str.charAt(str.length - 1)
let curValue = str;
if(laststr === '='){
curValue = str +' '+revdata;
}
return (
'div_class_calculator'>
'div_class_showdatabar'>
简易计算器
type="text"
value={curValue}
readOnly
/>
'div_class_buttonlist'>
{buttonlist}
);
}
}
/**
* @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(
{route}
,
document.getElementById('root')
);