老博文迁移,原地址
前言
在上文中我们学习了react的工作流程,知道了用户点击操作先是分发action,然后reducer根据接收到的action来做具体值的改变的这个曲折过程。 仔细看redux工作流的盆友一定发现了,上文中并没有用到最中心的store,那么下文中我们将引入新的东西:store 知识点: Provider createStore connect
store类似与vue的vuex,在vue中vuex是一个集中数据和方法的中心仓库,同样,store也有异曲同工之妙。
接下来我们开始真正地接触redux。
目录概览
npm install --save reduxnpm install --save react-redux
以下贴一个使用了store,connect等知识点的同样的计数器例子,只是这次的计数器的值是放在props里公共管理的
/*** @desc redux-example*/import React, { Component } from 'react'import ReactDOM from 'react-dom'import { createStore } from 'redux'import { Provider, connect } from 'react-redux'// UI组件/** [1]* 两个参数:* value:由props的得到* 方法:向外发出action*/class Counter extends Component {render() {const { value, onIncreaseClick } = this.propsreturn (<div><span>{value}</span><button onClick={onIncreaseClick}>Increase</button></div>)}}/*** [2] 定义映射 输入输出逻辑* 将UI组件的value值[props]映射到state* 将UI组件的方法映射到action 通过dispatch触发action*///将UI组件的props与redux的state映射function mapStateToProps(state) {return {value: state.count}}//将UI组件的props与redux的action映射function mapDispatchToProps(dispatch) {return {//用户的onIncreaseClick方法与action映射([3]定义action),通过dispatch触发reduceronIncreaseClick: () => dispatch(increaseAction)}}/*** [3] 给UI组件用connect()方法附上输入输出逻辑[逻辑],生成一个容器组件App*/const App = connect(mapStateToProps,mapDispatchToProps)(Counter)/*** [4] 定义action 纯函数 输入什么输出什么 收到的action返回一个type字段* dispatch拿着这个type字段去找reducer,reducer根据不同type匹配不同的动作 并最终返回一个新state*/// Actionconst increaseAction = { type: 'increase' }/*** [5] 定义reducer* 用户触发什么action对应返回一个经过reducer处理的新state,不同type对应不同的处理方式*/// Reducerfunction counter(state = { count: 0 }, action) {const count = state.countswitch (action.type) {case 'increase':return { count: count + 1 }default:return state}}/*** [6] 以reducer做为传入值生成一个store对象*/// Storeconst store = createStore(counter)/*** [7] 使用Provider组件在根组件外面包一层 App及其子组件就可以拿到state了*/ReactDOM.render(<Provider store={store}><App /></Provider>,document.getElementById('root'))
继续讲故事
接上文, 看了代码的可能会惊讶,哇,怎么一下多了这么多东西!
其实我们只是进入了真正的战斗状态而已哈哈。数据的管理思路还是没变。
以下说说此文中我们接触的真正的”战斗状态的redux“:
我们看看同样的计数器”这篇”较前文不同的部分:
|-- mapStateToProps|-- mapDispatchToProps|-- App = connect(mapStateToProps,mapDispatchToProps)(Counter)|-- store = createStore(counter)|-- <Provider store={store}><App /></Provider>
细心的盆友一定发现了,本组件Couter没有自身属性了,即state。它的参数和方法都来自于props。如果按照上文的比喻理解,可以认为,小县城连本地仓库和记录本地仓库数据的会计(或笔杆)都没啦!哦漏,现在小县城就仅仅是个”废城”啦,没有自身属性(展示的数据来源于外部),别人给我什么我就展示什么,也会和用户交互但是并不能改变展示的东西,只能发出一个action,传递这个动作的意义让别人来改变组件数据状态。俗称木偶组件
1. 现在理理故事背景:
小县城仍然想得到计算机王国的粮仓储值情况,县城的粮食收益会报告给王国粮食中心,县城的粮食值来源是”王国粮食中心 “。
2. UI组件和容器组件:
我们提到过,现在的小县城是木偶组件了,只负责展示数据和发出动作的action,但是为什么要把小城变为木偶组件?
我们想一下哈,王国有多个县城,多个县城的粮食情况都需要与粮食中心实时,相当于我们将粮食管理控制采取了中央集权制了,让一个完整的组件划分成UI和容器两类,UI专注展示,容器专注逻辑和数据处理。
3. 我们有的“实物”:
- 一个小县城Couter组件-UI组件(功能:展示数据 + 发出改变数据的action)
2.一个粮仓Store (王国的粮食储备中心,里面实时记录着本国的粮食数量) - 一个信号中转站 Reducer (可以实时读取到store的数据及里面生活着一个可以计算的会计师(方法))
我们还需要生成一个县城的容器组件,负责处理县城的数据和逻辑交互,这个组件可以拿到仓库store的数据,同时也可以将UI组件发出的action传给reducer
4.建立联系:
建立县城与粮食中心的联系:
const App = connect(mapStateToProps,//将redux的state与UI组件的props映射mapDispatchToProps//将redux的action与UI组件的props映射)(Counter)
说明:
- store是个仓库,它的state值被下发到各县城组件,作为县城组件的props。即不要被state和props绕晕了
- connect 奇怪的写法,其实这是个函数式编程写法,在这里可以姑且理解为输入的两个逻辑通过connect被完美地糅合在一起,并携带上counter这个UI组件一起生成了容器组件。
最后
通过上文我们学习了真实的redux,我们总结下如下几个我认为比较重要的知识点:
0. 容器组件和UI组件
- state和props区别
- redux和react关系
笔记详情见:react初学笔记
在回顾了本文学习的redux知识之余,我们跳出redux这个框可以思考如下几个问题:
1.redux的存在意义?数据集中管理方案的其他出路?
2.木偶、容器组件的意义?(我们为什么要将好端端的一个组件分成木偶组件和容器组件?)即为什么推荐无状态组件?
code例子来源:simplest-redux-example
