老博文迁移,原地址

前言

在上文中我们学习了react的工作流程,知道了用户点击操作先是分发action,然后reducer根据接收到的action来做具体值的改变的这个曲折过程。 仔细看redux工作流的盆友一定发现了,上文中并没有用到最中心的store,那么下文中我们将引入新的东西:store 知识点: Provider createStore connect

store类似与vue的vuex,在vue中vuex是一个集中数据和方法的中心仓库,同样,store也有异曲同工之妙。
接下来我们开始真正地接触redux。

目录概览

  1. npm install --save redux
  2. npm install --save react-redux

以下贴一个使用了store,connect等知识点的同样的计数器例子,只是这次的计数器的值是放在props里公共管理的

  1. /**
  2. * @desc redux-example
  3. */
  4. import React, { Component } from 'react'
  5. import ReactDOM from 'react-dom'
  6. import { createStore } from 'redux'
  7. import { Provider, connect } from 'react-redux'
  8. // UI组件
  9. /** [1]
  10. * 两个参数:
  11. * value:由props的得到
  12. * 方法:向外发出action
  13. */
  14. class Counter extends Component {
  15. render() {
  16. const { value, onIncreaseClick } = this.props
  17. return (
  18. <div>
  19. <span>{value}</span>
  20. <button onClick={onIncreaseClick}>Increase</button>
  21. </div>
  22. )
  23. }
  24. }
  25. /**
  26. * [2] 定义映射 输入输出逻辑
  27. * 将UI组件的value值[props]映射到state
  28. * 将UI组件的方法映射到action 通过dispatch触发action
  29. */
  30. //将UI组件的props与redux的state映射
  31. function mapStateToProps(state) {
  32. return {
  33. value: state.count
  34. }
  35. }
  36. //将UI组件的props与redux的action映射
  37. function mapDispatchToProps(dispatch) {
  38. return {
  39. //用户的onIncreaseClick方法与action映射([3]定义action),通过dispatch触发reducer
  40. onIncreaseClick: () => dispatch(increaseAction)
  41. }
  42. }
  43. /**
  44. * [3] 给UI组件用connect()方法附上输入输出逻辑[逻辑],生成一个容器组件App
  45. */
  46. const App = connect(
  47. mapStateToProps,
  48. mapDispatchToProps
  49. )(Counter)
  50. /**
  51. * [4] 定义action 纯函数 输入什么输出什么 收到的action返回一个type字段
  52. * dispatch拿着这个type字段去找reducer,reducer根据不同type匹配不同的动作 并最终返回一个新state
  53. */
  54. // Action
  55. const increaseAction = { type: 'increase' }
  56. /**
  57. * [5] 定义reducer
  58. * 用户触发什么action对应返回一个经过reducer处理的新state,不同type对应不同的处理方式
  59. */
  60. // Reducer
  61. function counter(state = { count: 0 }, action) {
  62. const count = state.count
  63. switch (action.type) {
  64. case 'increase':
  65. return { count: count + 1 }
  66. default:
  67. return state
  68. }
  69. }
  70. /**
  71. * [6] 以reducer做为传入值生成一个store对象
  72. */
  73. // Store
  74. const store = createStore(counter)
  75. /**
  76. * [7] 使用Provider组件在根组件外面包一层 App及其子组件就可以拿到state了
  77. */
  78. ReactDOM.render(
  79. <Provider store={store}>
  80. <App />
  81. </Provider>,
  82. document.getElementById('root')
  83. )

继续讲故事

接上文, 看了代码的可能会惊讶,哇,怎么一下多了这么多东西!
其实我们只是进入了真正的战斗状态而已哈哈。数据的管理思路还是没变。
以下说说此文中我们接触的真正的”战斗状态的redux“:
我们看看同样的计数器”这篇”较前文不同的部分:

  1. |-- mapStateToProps
  2. |-- mapDispatchToProps
  3. |-- App = connect(
  4. mapStateToProps,
  5. mapDispatchToProps
  6. )(Counter)
  7. |-- store = createStore(counter)
  8. |-- <Provider store={store}>
  9. <App />
  10. </Provider>

细心的盆友一定发现了,本组件Couter没有自身属性了,即state。它的参数和方法都来自于props。如果按照上文的比喻理解,可以认为,小县城连本地仓库和记录本地仓库数据的会计(或笔杆)都没啦!哦漏,现在小县城就仅仅是个”废城”啦,没有自身属性(展示的数据来源于外部),别人给我什么我就展示什么,也会和用户交互但是并不能改变展示的东西,只能发出一个action,传递这个动作的意义让别人来改变组件数据状态。俗称木偶组件

1. 现在理理故事背景:
小县城仍然想得到计算机王国的粮仓储值情况,县城的粮食收益会报告给王国粮食中心,县城的粮食值来源是”王国粮食中心 “。

2. UI组件和容器组件:
我们提到过,现在的小县城是木偶组件了,只负责展示数据和发出动作的action,但是为什么要把小城变为木偶组件?
我们想一下哈,王国有多个县城,多个县城的粮食情况都需要与粮食中心实时,相当于我们将粮食管理控制采取了中央集权制了,让一个完整的组件划分成UI和容器两类,UI专注展示,容器专注逻辑和数据处理。

3. 我们有的“实物”:

  1. 一个小县城Couter组件-UI组件(功能:展示数据 + 发出改变数据的action)
    2.一个粮仓Store (王国的粮食储备中心,里面实时记录着本国的粮食数量)
  2. 一个信号中转站 Reducer (可以实时读取到store的数据及里面生活着一个可以计算的会计师(方法))

我们还需要生成一个县城的容器组件,负责处理县城的数据和逻辑交互,这个组件可以拿到仓库store的数据,同时也可以将UI组件发出的action传给reducer

4.建立联系:
建立县城与粮食中心的联系:

  1. const App = connect(
  2. mapStateToProps,//将redux的state与UI组件的props映射
  3. mapDispatchToProps//将redux的action与UI组件的props映射
  4. )(Counter)

说明:

  1. store是个仓库,它的state值被下发到各县城组件,作为县城组件的props。即不要被state和props绕晕了
  2. connect 奇怪的写法,其实这是个函数式编程写法,在这里可以姑且理解为输入的两个逻辑通过connect被完美地糅合在一起,并携带上counter这个UI组件一起生成了容器组件。

最后

通过上文我们学习了真实的redux,我们总结下如下几个我认为比较重要的知识点:
0. 容器组件和UI组件

  1. state和props区别
  2. redux和react关系

笔记详情见:react初学笔记

在回顾了本文学习的redux知识之余,我们跳出redux这个框可以思考如下几个问题:
1.redux的存在意义?数据集中管理方案的其他出路?
2.木偶、容器组件的意义?(我们为什么要将好端端的一个组件分成木偶组件和容器组件?)即为什么推荐无状态组件?

code例子来源:simplest-redux-example