在之前的章节 Flux 与 Redux 当中,我们介绍了 Flux
与 Redux
的一些基本概念和它们之间的一些区别,今天我们在来看看另外一个和它们比较类似的 React-Redux
,然后在简单汇总一下,比较一下它们三者之间的区别,不过在看 React-Redux
的内容之前,我们先来简单的回顾一下上一章的内容
Flux
传统的 Redux
简化了 Flux
的流程,一般 Flux
的流程是
view
触发action
中的方法action
发送dispatch
store
接收新的数据进行合并,触发view
中绑定在store
上的方法- 通过修改局部
state
,改变局部view
Redux
Redux
就是 Flux
思想在 React
当中的实现,它的流程是
view
直接触发dispatch
- 将
action
发送到reducer
中后,根节点上会更新props
,改变全局view
Redux
将view
和store
的绑定从手动编码中提取出来,形成了一个统一的规范最后放在了自己的体系中- 而在基本的
Redux
流程中,action
只是充当了一个类似于topic
之类的角色(类似type
属性),reducer
会根据这个topic
确定需要如何返回新的数据 - 数据的结构处理也从
store
中移到了reducer
中
Redux
数据流如下图所示
总之记住一句话
Redux
的基本原理实际上就是围绕着store
进行的
这个 store
不是 Flux
中的 store
,而是 Redux
提供的 createStore
方法创建的 store
createStore
方法接收reducer
函数和初始化的数据(currentState
),并将这两个参数并保存在store
中createStore
时传入的reducer
方法会在store
的dispatch
被调用的时候再被调用,接收store
中的state
和action
,根据业务逻辑修改store
中的state
Store
store
中包含 subscribe()
、dispatch()
、getState()
和 replaceReducer()
这四个方法
- 其中,
subscribe
和dispatch
顾名思义就是订阅和发布的功能 subscribe
接收一个回调(listener
),当dispatch
触发时,执行reducer
函数去修改当前数据(currentState
),并执行subscribe
传入的回调函数(listener
)- 而
getState
是获取当前store
的state(currentState)
- 至于
replaceReducer
方法,就是动态替换reducer
函数(一般使用较少)
Middleware
下面我们再来简单的了解一下 Redux
中的 middleware
,Redux
中的 middleware
简单来说只是针对于 dispatch
方法做了 middleware
处理,也就是说,只接受一个 action
对象,例如官方示例中的
1 | const createStoreWithMiddleware = applyMiddleware( |
Redux
的 middleware
用 reduceRight
的方法,将 applyMiddleware
方法中的参数串起来,原始的 dispatch
方法会最后执行,比如下图所示
而如果需要自定义 middleware
只需要注意这个 middleware
只接收一个 action
,执行后也需要返回一个 action
,如果需要执行下一步,调用 next(action)
即可
关于中间件的概念,我们会在 中间件 这一章当中详细的来进行介绍
React-Redux
在简单回顾完了 Flux
与 Redux
的一些基本概念之后,下面我们就来看看 React-Redux
,其实简单来说 React-Redux
是对 Redux
流程的一种封装,使其可以适配与 React
的代码结构,React-Redux
首先提供了一个 Provider
组件(用来包裹),可以将从 createStore
返回的 store
放入 Context
中,使子集可以获取到 store
并进行操作
1 | <Provider store={store}> |
大致逻辑如下
- 首先
React-Redux
提供了connect
方法,将原始根节点包装在Connect
下,在Connect
中的state
存储不可变对象,并将state
对象中的props
和store
中的dispatch
函数传递给原始根节点 Connect
在componentDidMount
中,给store
添加listener
方法(handleChange
),每当store
中的dispatch
被调用时执行handleChange
- 而
handleChange
会去修改state
中的porps
,使原始根节点重新render
,并且Connect
已经在shouldComponentUpdate
实现了PureRender
功能
handleChange
更新 state
中的 props
逻辑主要由三个函数构成,这三个函数都由 connect
方法传入(前两个参数用的较多)
1 | connect( |
- 第一个函数接收
store
中state
和props
,使页面可以根据当前的store
中state
和props
返回新的stateProps
- 第二个函数接收
store
中的dispatch
和props
,使页面可以复写dispatch
方法,返回新的dispatchProps
- 第三个函数接收前两个函数生成的
stateProps
和dispatchProps
,在加上原始的props
合并成新的props
并传给原始根节点的props
React-Redux
中的流程如下图
首先 view
触发 dispatch
然后进入 reducer
,修改 store
中的 state
,再将新的 state
和 props
传入 handleChange
中,生成更符合页面的 props
,最后传给原始根节点重新 render
,下面我们就深入的来了解一下 React-Redux
当中的相关概念,React-Redux
将所有组件分成了以下两大类
UI
组件(presentational component
)- 容器组件(
container component
)
下面我们就分别来看这两类组件
UI 组件
UI
组件有以下几个特征
- 只负责
UI
的呈现,不带有任何业务逻辑 - 没有状态(即不使用
this.state
这个变量) - 所有数据都由参数(
this.props
)提供 - 不使用任何
Redux
的API
因为不含有状态,UI
组件又称为纯组件,即它像纯函数一样,纯粹由参数决定它的值
容器组件
容器组件的特征恰恰相反
- 负责管理数据和业务逻辑,不负责
UI
的呈现 - 带有内部状态
- 使用
Redux
的API
总之,只要记住一句话,
UI
组件负责UI
的呈现,容器组件负责管理数据和逻辑
在 React-Redux
当中比较常用的 API
就两个,一个是提供的一个组件 <Provider>
,另一个就是 connect()
方法
connect()
React-Redux
提供 connect
方法,用于从 UI
组件生成容器组件,connect
的意思就是将这两种组件连起来
1 | import { connect } from 'react-redux' |
上面代码中,connect
方法接受两个参数,mapStateToProps
和 mapDispatchToProps
,它们定义了 UI
组件的业务逻辑,前者负责输入逻辑,即将 state
映射到 UI
组件的参数(props
),后者负责输出逻辑,即将用户对 UI
组件的操作映射成 action
connect()
方法有两个比较重要的参数,mapStateToProps
和 mapDispatchToProps
(都是函数)
mapStateToProps
官方解释如下
- 如果你传入了这个参数,那么这个组件将会注册
Redux
的store
的更新信息(简单的可以理解为虚拟DOM
那样,即变化了会自动更新) - 即意味着无论什么时候
store
更新了,mapStateToProps
函数将会被调用 mapStateToProps
的返回值必须是一个plain object
(简单的对象),这个对象将和组件的props
融合,也就是说返回的对象中的key
将自动成为组件的props
中的成员(就比如上例中的App
组件中的参数)- 如果不想订阅
store
的更新,可以不用传入该参数,此时使用null
来占位即可(不能不传)
抛开官方那些比较繁琐的解释,本质上 mapStateToProps()
就是一个函数,它的作用就是建立一个从(外部的)state
对象到(UI
组件的)props
对象的映射关系,作为函数 mapStateToProps
执行后应该返回一个对象,里面的每一个键值对就是一个映射
1 | const mapStateToProps = (state) => { |
这个函数内部返回的键值对可以被 connect()
传入的参数组件所拿到(上例中的 App
),即 App
组件当中可以拿到传递过去的参数(state
)(当作 props
来使用),而 </Provider>
中传递的 store
之所以可以被全局使用,也是依靠的这个函数(间接的通过参数传递过去了),然后最重要的一点就是,它可以得到全局唯一的 store
中的 state
mapDispatchToProps
mapDispatchToProps
是 connect
函数的第二个参数,用来建立 UI
组件的参数到 store.dispatch
方法的映射,也就是说,它定义了哪些用户的操作应该当作 action
,传给 store
,官方解释如下
- 如果传入了第二个参数,并且是一个函数,那么这个函数将获得
dispatch
方法(可以发出action
,也可以导致state
(store
) 改变) - 即
connect()
方法第一个参数来获得state
(但是不能修改),第二个参数可以用来修改state
(但是最终都传递回了App
组件)
它接收参数 dispatch
(dispatch
是 store
中用来分发命令的 API
,这里简化了),返回一个总的 actions
清单对象
1 | const mapDispatchToProps = (dispatch) => { |
<Provider>
connect
方法生成容器组件以后,需要让容器组件拿到 state
对象,才能生成 UI
组件的参数,React-Redux
提供 Provider
组件,可以让容器组件拿到 state
1 | import React from 'react' |
在上面的示例当中,Provider
在根组件外面包了一层,这样一来 App
的所有子组件就默认都可以拿到 state
了