老博文迁移,原地址

前言

之前的例子都是写的计数器,加一减一的功能,我们大致弄懂了redux分层和store数据管理,下面我们将结合现有知识写一个终极版的计算器。以此巩固所学知识 知识点:redux分层,react-router,一些算法及数据结构知识[栈 + 中缀转后缀]

首段祭出源码地址:
github:https://github.com/EmilyYoung71415/LearnReactDemo

效果图

04—计算器Demo - 图1

项目结构

  1. ├──redux-demo/ * 计算器Demo
  2. |
  3. |————src/ * 主程序
  4. ├─Components * 所有组件
  5. ├─Calculator * 计算器
  6. ├─Counter * 计数器
  7. └─StudyDemos * 学习的一些有帮助的demo
  8. └─备份文件夹 * 笔记 等我写完博客就清
  9. ├─Error * 错误组件
  10. ├─Redux * Redux
  11. ├─Action
  12. ├─Containers
  13. ├─Reducer
  14. └─Store
  15. ├─Router * 路由
  16. └─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

源码解读

  1. import React, {Component} from 'react';
  2. import { createStore } from 'redux'
  3. import { connect } from 'react-redux'
  4. import suffixExpression from './stack'
  5. import '../../Style/calcuator.css'
  6. const KEYVALUE = [
  7. {value: '7'},
  8. {value: '8'},
  9. {value: '9'},
  10. {value: '←'},
  11. {value: 'C'},
  12. {value: '4'},
  13. {value: '5'},
  14. {value: '6'},
  15. {value: '*'},
  16. {value: '/'},
  17. {value: '1'},
  18. {value: '2'},
  19. {value: '3'},
  20. {value: '+'},
  21. {value: '-'},
  22. {value: '0'},
  23. {value: '00'},
  24. {value: '.'},
  25. {value: '%'},
  26. {value: '='},
  27. {value: '('},
  28. {value: ')'}
  29. ];
  30. class MyCalculator extends Component {
  31. constructor(props){
  32. super(props);
  33. this.state = {
  34. valueText: '0' //实时更新用户输入的值
  35. }
  36. }
  37. handleValueInput(data) {
  38. let oldState = this.state.valueText;
  39. //传入当前文本框的值和当前按钮按下的值,调用checkClickType依据不同的按钮值做不同的反应,返回新的值。
  40. let rev = this.checkClickType(oldState,data);
  41. let newState = {};
  42. newState.valueText = rev;
  43. this.setState( newState)
  44. }
  45. checkClickType(oldvalue,value){
  46. switch (value) {
  47. case '=':
  48. let resultbefore = oldvalue + ' =' ;
  49. //向外分发action
  50. this.props.equalClick(oldvalue);
  51. return resultbefore;
  52. case '←':
  53. //删除最后一位
  54. oldvalue = oldvalue.substring(0,oldvalue.length-1)
  55. return oldvalue;
  56. case 'C':
  57. oldvalue = '0';
  58. return oldvalue;
  59. case '+':
  60. case '-':
  61. case '/':
  62. case '*':
  63. case '(':
  64. case ')':
  65. return oldvalue + ' ' +value + ' ';//运算符与操作数以空格为分割
  66. default://一般数字
  67. if(oldvalue === '0'){//清零
  68. oldvalue = ''
  69. }
  70. return oldvalue + value
  71. }
  72. }
  73. render() {
  74. const {revdata} = this.props;//获得最新的结果值
  75. let buttonlist = [];
  76. KEYVALUE.forEach(data => {
  77. buttonlist.push(
  78. <button className='div_class_button'
  79. key={data.value}
  80. onClick = {this.handleValueInput.bind(this,data.value)}
  81. >{data.value}</button>
  82. );
  83. });
  84. //取当前input框字符串的最后一个字符 如果是等于符号则 运算过程+结果
  85. let str = this.state.valueText;
  86. let laststr = str.charAt(str.length - 1)
  87. let curValue = str;
  88. if(laststr === '='){
  89. curValue = str +' '+revdata;
  90. }
  91. return (
  92. <div className='div_class_calculator'>
  93. <div className='div_class_showdatabar'>
  94. <h1>简易计算器</h1>
  95. <input type="text"
  96. value={curValue}
  97. readOnly
  98. />
  99. </div>
  100. <div className='div_class_buttonlist'>
  101. {buttonlist}
  102. </div>
  103. </div>
  104. );
  105. }
  106. }
  107. /**
  108. * @func 模块--container
  109. * @desc 定义映射
  110. */
  111. //将UI组件的props与redux的state映射
  112. function mapStateToProps(state) {
  113. return {
  114. revdata: state.revdata
  115. }
  116. }
  117. //将UI组件的props与redux的action映射
  118. function mapDispatchToProps(dispatch) {
  119. return {
  120. //用户的onIncreaseClick方法与action映射([3]定义action),通过dispatch触发reducer
  121. equalClick: (value) => dispatch(getResult(value))
  122. }
  123. }
  124. /**
  125. * @func 模块--action
  126. * @desc
  127. */
  128. const EQUEALBTN = 'EQUEALBTN'; //常规按钮
  129. const ActionGenerator = (type, num) => (num) => {
  130. let action = { type, num : num }
  131. return action
  132. }
  133. const getResult = ActionGenerator(EQUEALBTN, null);
  134. /**
  135. * @func 模块--connect
  136. */
  137. const App = connect(
  138. mapStateToProps,
  139. mapDispatchToProps
  140. )(MyCalculator)
  141. /**
  142. * @func 模块--reducer
  143. * @desc 根据action 返回新的state
  144. */
  145. function getRev(state = { revdata: 0 }, action) {
  146. //action.num即是等号前面的字符串
  147. switch (action.type) {
  148. case EQUEALBTN:
  149. //let test = '1 + 78 + 22 + ( 10 - 2 ) * 6';
  150. let rev = suffixExpression(action.num)//具体的计算处理,我采用的是中缀转后缀计算方法。
  151. return { revdata: rev }
  152. default:
  153. return state
  154. }
  155. }
  156. /**
  157. * @func 模块--store
  158. * @desc 以reducer生成store对象
  159. */
  160. const store = createStore(getRev)
  161. export {
  162. store,
  163. App
  164. };
  1. //index.js
  2. import React from 'react';
  3. import ReactDOM from 'react-dom';
  4. import { Provider } from 'react-redux'
  5. import {store,App} from './Components/Calculator/calculatorAll';
  6. ReactDOM.render(
  7. <Provider store={store}>
  8. {route}
  9. </Provider>,
  10. document.getElementById('root')
  11. );