react 3 - redux
前言
“如果你不知道是否需要 Redux, 那就是不需要它. “
虽然有这样一句话, 可是作为开发, 却是必须要学会使用的, 只有你会了以后, 才可以来探讨你的项目里面是否需要Redux
.
Flux
react
为我们的整个应用提供了界面搭建的方法, 如果你的组件够多, 那么就可以像拼积木一样一点点搭建你的应用. 而react
的Render
是一个纯函数, 只要组件有同样的state
和props
, 那么你就会得到同样的view
. 而Flux
的理念就是使state
变成不可写, 而是由action
分发的方式在内部处理好数据, 再传给state
, 而避免组件自己随意修改state
造成不可预知的问题.
Redux
Redux
是Flux
思想的一种实现方式, 与react
结合使用更加方便. 而Redux
的设计思想很简单, 就两句话:
- 应用是一个状态机, 视图与状态是一一对应的.
- 所有的状态, 保存在一个对象里面.
基本概念
store
在整个应用中, 只有一个store
, 它保存着所有数据. 如果想得到某个时间点的数据, 就需要对store
生成快照, 生成快照的方法是store.getState()
, 那么这个快照自然就叫做state
了. 注意, 此state
和组件里面的state
不是一个概念, 也不会有直接的交集. Redux
规定, 一个state
对应一个view
, 只要state
是相同的, 那么生成的view
应该也是一样的.
action
state
已经有了数据, 那么自然就需要有改变数据的方式, 这个方式就是action
, 同时也是唯一的方式. 对于view
, 不能直接接触到state
和store
, 只能通过发送action
的方式来改变store
. action
是一个对象, 其中type
属性是必须的, 其他属性都属于携带的信息, 一般通过payload
来传递.
1 | const action = { |
action creator
action
既然是一个对象, 那么创建这个对象肯定不会是手写, 而是有一个方法来自动生成, 那么生成这个action
的方法即action creator
.
1 | static addTodo(v) { |
addTodo
函数即为ADD_TODO
的action creator
.
dispatch
有了action creator
和生成的action
, 那么怎么让store
知道这个action
呢, store.dispatch(action)
就是用来发出action
的唯一方法.
reducer
声明和使用
当调用store.dispatch
之后, 怎么去改变state
呢, 这就是reducer
的功能了. 每个reducer
都是一个函数, 它的参数是当前state
和action
, 返回值是一个新的state
, 这样view
就会根据新的state
去绘制界面了.
1 | const defaultState = 0; |
当创建好reducer
函数之后, 需要让store
知道这个函数, 然后store
会在调用dispatch
时, 自动触发所有的reducer
函数. createStore
中的reducer
参数就是让store
去连接指定的reducer
方法.
纯函数
reducer
必须是一个纯函数, 意义是每次执行会得到相同的结果. 所以不能调用Data.now()
或Math.random()
等方法. 但是由于是纯函数, 基于函数式编程的理念, 是不能改变原始对象的. 所以如果是对象, 返回值应该是return { ...state, ...newState }
. 如果对象结构层级特别深, 那么创建newState
就会是一个很麻烦的事情, 而且可能会不小心改变state
后也会出现其他错误, 这就是为什么需要immutable
的原因.
subscribe
store
提供subscribe
方法来设置一个监听函数, 只要state
发生改变, 那么就会执行这个监听函数. subscribe
方法返回一个解除监听的函数unsubscribe
, 执行unsubscribe
就可以取消监听.
1 | let unsubscribe = store.subscribe(() => console.log(store.getState())); |
工具函数
combineReducers
一般都会用到的函数, 如果你的reducer
有很多个, 那么你就需要用到这个函数创建出一个rootReducers
对象来提供给store
连接reducer
函数.
1 | const RootReducer = combineReducers({ |
connect
connect
方法可以为你的展示组件创建对应的容器组件. connect
方法签名为connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
.
mapStateToProps(state, ownProps) : stateProps
mapDispatchToProps(dispatch, ownProps): dispatchProps
mergeProps(stateProps, dispatchProps, ownProps): props
options
为选项, 使用到的地方很少
mapStateToProps(state, ownProps) : stateProps
从名字可以看出, 此函数是把state
的属性赋给props
(此处state
非组件state
, 而是store
的state
). 当state
变化, 或者ownProps
变化的时候, mapStateToProps
都会被调用(此处ownProps
是上级组件的props
传入到connect
生成的高阶组件的props
, 不是被connect
的组件的ownProps
), 计算出一个新的props
更新给组件, 并触发组件的componentWillReceiveProps
方法.
1 | const mapStateToProps = (state, ownProps) => { |
mapDispatchToProps(dispatch, ownProps): dispatchProps
从名字也可以看出来, 是把dispatch
方法映射到props
上, 不过没有mapStateToProps
那么直观. 在ownProps
变化时, mapDispatchToProps 也会再次执行, 计算出一个新的值更新到props
上.
1 | const mapDispatchToProps = (dispatch, ownProps) => { |
传入这个函数以后, 在组件的props
上, 就可以使用addTodo
方法了.redux
提供了bindActionCreators
用来快速将action
生成可调用的函数.
1 | const mapDispatchToProps = (dispatch, ownProps) => { |
mergeProps(stateProps, dispatchProps, ownProps): props
这个函数就是用来将上面两个函数生成的值复制到props
里面的函数, 如果不传这个函数, 默认调用的是Object.assign
方法.
createStore
createStore(reducer, initialState, enhancer): store
是创建store
的唯一方法, reducer
即全部的reducers
, initialState
为store
的初始化值, enhancer
即可以让我们生成加强版本的store
的方法.
1 | //createStore中enhancer有关的源码 |
可以看到enhancer
应该是一个方法, 接收createStore
作为参数, 返回一个新createStore
的方法, 再传入reducer, preloadedState
参数执行.
applyMiddleware
applyMiddleware
方法生成的函数即enhancer
所表示的值, 从enhancer
实现可以看出, applyMiddleware
有两种调用方式.
1 | //1 |
ReduxThunk
重点说说ReduxThunk
中间件, 它是与后台交互的必备中间件, 使我们在dispatch
中可以发出异步的action
. 它的源码也是十分简单.
1 | function createThunkMiddleware(extraArgument) { |
使用的时候也很简单.
1 | static addTodo(v) { |
Provider
Provider
严格来说应该是组件, 不过class
本质也是function
, 所以放在一起了. Provider
组件即让整个应用都可以直接拿到store
的组件. 原理是react
中的context
.
1 | //源码 |
react 3 - redux