因为公司项目的技术栈是 React,与之配套的公共状态管理的库是 Redux,最近也研究了其中的原理。因为我以前是硬背 Redux 的用法,时间搁久了老是忘记如何使用。每次要用的时候,就去翻文档,不只效率低下,用起来也感受到恶心本身了。搞清楚了背后的机制,写起来就很顺手。相信你们弄懂的基本的实现细节,用起来也会驾轻就熟。react
首先思考 Redux 解决了什么问题,为何要使用它。每当有一些数据要在不少组件中使用的时候,而这些组件跨层级时,props 就再也不适合。就须要把这些数据提取出来放在一个模块,咱们把它称为公共状态,每当数据改变时,每当数据改变时,它会通知订阅它的说有组件。web
要想实现这样的共享的数据,首先想到的必是建一个全局模块,里面存放不少公共的数据。就像这样redux
const obj = {
count: 0
}
复制代码
当状态须要改变的时候,直接显示的赋值,固然,这里要经过观察者模式数据已经改变。不过这样确定很差闭包
既然是共享数据,外部又不能直接改变它,固然就是一个闭包啦。没错,Redux 内部这样的一个机制,经过 store 的一些方法,咱们能够搭出大体的骨架app
function createStore() {
// 公共的状态
let currentState = {}
// getter
function getState() {
return currentState
}
// setter
function dispatch() {
}
function subscribe() {
}
return {
getState,
dispatch,
subscribe
}
}
复制代码
毫无疑问,每次调用 getState 就能够拿到最新的 currentState 的值异步
在 Redux 里,每当要改变数据,都是调用 dispatch 一个 action,根据 actionType 的值来返回不一样的 state,好了,咱们就能够这样写编辑器
function createStore() {
// 公共的状态
let currentState = {}
// getter
function getState() {
return currentState
}
// setter
function dispatch(action) {
swicth(action.type) {
case "increament":
return {
...currentState,
count: currentState.count + 1
}
default:
return currentState
}
}
function subscribe() {
}
return {
getState,
dispatch,
subscribe
}
}
复制代码
这样就实现了一个 dispatch,但这样写很差,咱们把 switch 这样的逻辑能够提取成一个函数,这也是所说的 reducer,那么,代码就成了这样ide
const initialState = {
count: 1
}
function reducer(state = initialState, {type, payload}) {
switch (type) {
case 'increament':
return {
...state,
count: count + payload
}
case 'decrement':
return {
...state,
count: count - payload
}
default:
return initialState
}
}
function createStore() {
// 公共的状态
let currentState = {}
// getter
function getState() {
return currentState
}
// setter
function dispatch(action) {
currentState = reducer(currentState, action)
}
function subscribe() {
}
return {
getState,
dispatch,
subscribe
}
}
复制代码
在咱们建立 store 的时候,会初始化一次 state,也就是内部会 dispatch 一次,于是函数
function createStore() {
// 公共的状态
let currentState = {}
// getter
function getState() {
return currentState
}
// setter
function dispatch(action) {
currentState = reducer(currentState, action)
}
dispatch({ type: '@@REDUX_INIT' })
function subscribe() {
}
return {
getState,
dispatch,
subscribe
}
}
复制代码
到此,即完成了获取和改变状态的功能,验证一下代码ui
const store = createStore(reducer)
console.log(store.getState().count) // 1
store.dispatch({type: 'increament', payload: 5})
console.log(store.getState().count) // 6
复制代码
尽管如此,当数据改变了,仍是不能通知我视图更新。这里须要监听数据的变化,用到了观察者模式,也有人说是发布订阅模式,这两种模式基本的思想一致,但仍是有一些区别的,这里观察者模式更加准确。状态数据至关于被观察者,在 createStore 中,咱们要维护一个观察者的队列,当执行 subscribe时,把他们放入队列,dispatch的时候,执行队列里的回调
function createStore() {
// 公共的状态
let currentState = {}
// 存放观察者的队列
let observers = []
// getter
function getState() {
return currentState
}
// setter
function dispatch(action) {
currentState = reducer(currentState, action)
observers.foreach(fn => fn())
}
dispatch({ type: '@@REDUX_INIT' })
function subscribe(fn) {
observers.push(fn)
}
return {
getState,
dispatch,
subscribe
}
}
复制代码
如此以来,就完成了数据变化,通知组件改变视图的功能,模拟组件运行代码
const store = createStore(reducer)
store.subscribe(() => console.log('组件1收到通知', store.getState()))
store.subscribe(() => console.log('组件2收到通知', store.getState()))
store.dispatch({ type: 'increament', payload: 5 })
复制代码
一个最简单的 redux 的应用已经完成,这里还缺饭了大量的参数类型判断,异常处理,异步action、中间件等等。但基本的原理大体相同
在 react 的项目中,尽管 redux 能实现咱们的需求,但写法太过冗余,和 react 的组件写法也不太契合,因而,就有了 react-redux。简单来讲,它提供了两大功能:
Provider原理是把 store 绑定在 context 上,至于 context,能够为子孙组件的上下文提供 store 对象
class Provider extends Component {
static childContextTypes = {
store: PropTypes.shape({
subscribe: PropTypes.func.isRequired,
dispatch: PropTypes.func.isRequired,
getState: PropTypes.func.isRequired
}).isRequired
}
constructor(props) {
super(props);
this.store = props.store;
}
getChildContext() {
return {
store: this.store
}
}
render() {
return this.props.children
}
}
复制代码
connect 是一个高阶函数,接收 mapStateToProps, mapDispatchToProps 为参数,而且它返回一个高阶组件。其中,这两个参数都是函数,mapStateToProps 接收 state 返回一个对象,这搞清楚了,mapDispatchToProps 接收一个 dispatch 返回一个对象,咱们再来实现它:
// 形如 mapTostate、mapDispatchToProps
const mapStateToProps = state => {}
const mapDispatchToProps = dispatch => {}
function connect(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
class Connect extends Component{
constructor(props) {
super(props)
this.state = mapStateToProps(store.getState())
this.mapDispatch = mapDispatchToProps(store.dispacth)
}
ComponentDidMount() {
}
<WrappedComponent {...this.props} {this.state} {this.mapDispatch} />
}
return Connect
}
}
复制代码
知道这里,咱们经过高阶函数把一些属性挂载到了高阶组件的 props 上,接下来就能够经过 this.props.xxx 调用。此时,咱们被 connect 包裹的新组件的 props 上虽然有了值,可是还不具有自动更新的功能,继续改进 connect
function connect(mapStateToProps, mapDispatchToProps) {
return function(WrappedComponent) {
class Connect extends Component{
constructor(props) {
super(props)
this.state = mapStateToProps(store.getState())
this.mapDispatch = mapDispatchToProps(store.dispacth)
}
ComponentDidMount() {
// 若是 store 中的状态改变会执行回调函数,此时新获取的 mappedState 和旧的作对比,如如有变化,就setState
this.unsub = store.subscribe(() => {
const mappedState = mapStateToProps(store.getState());
if(shallowEqual(this.state, mappedState)) {
return;
}
this.setState(mappedState);
});
}
<WrappedComponent {...this.props} {this.state} {this.mapDispatch} />
}
return Connect
}
}
复制代码
如此,一个基本功能的 react-redux 已经实现。