毫无疑问,redux是react和RN开发中必不可少的重要组成,很多人使用它的时候,都是照搬,代码写完了,问他具体原理,鲜有人能够解释清楚....
与我而言,这么多年的学习生涯中,学习一项新技能的时候,必定会追根溯源,搞清楚原理和设计初衷,这样不仅更容易理解学习,而且会使用的更加得心应手,出现问题更容易知道从哪里下手解决问题,所以,大周末早上来重新梳理redux这个知识。为什么是重新?因为其实已经快速的学习过几遍了,但是真正的应用在项目中,或者说看别人已经完成的大型项目的时候,其实还是一脸懵逼,总在心里嘀咕,这套流程是怎么运作的?整个数据是怎么流通的???得到的答案总是❌,说到底还是没有完全搞懂redux这个东西。所以今天来从头开始,一步步深入其内部,深入学习,并且记录下来,供自己随时翻阅。花不多说,开始吧!
下面以计数器为例,步步深入理解学习。
原始实现:当没有redux的时候,我们最早期实现大抵是这样的:
import React, { PureComponent } from 'react';
import store from '../store';
import {
addAction
} from '../store/actionCreators'
export default class Home extends PureComponent {
constructor(props) {
super(props);
this.state = {
counter: store.getState().counter
}
}
componentDidMount() {
this.unsubscribue = store.subscribe(() => {
this.setState({
counter: store.getState().counter
})
})
}
componentWillUnmount() {
this.unsubscribue();
}
render() {
return (
<div>
<h1>Home</h1>
<h2>当前计数: {this.state.counter}</h2>
<button onClick={e => this.increment()}>+1</button>
<button onClick={e => this.addNumber(5)}>+5</button>
</div>
)
}
increment() {
store.dispatch(addAction(1));
}
addNumber(num) {
store.dispatch(addAction(num));
}
}
这个很直观,也很容易理解,就是从零开始,完全手动实现这个需求,这样完全OK,可以实现功能,但是这样会有一个问题,假如我有另一个组件,也要实现类似的功能,怎么办呢?
- easy!马上动手,再创建一个组件About,一通操作,完成了,大概是上图中的这个样子,看一下效果会发现,其实很多代码是大同小异的,假如一个大型的项目中,成篇的写这些代码,一方面效率很低,更深层的来说,这样的代码非常不优雅,那么我们抽取一下
抽取公共部分:
分析下来,我们决定将一些公共的代码抽取出来
类似上面这个效果,将公共的部分放在一个connect函数里面,这个connect会把这些抽出来的代码连接到Redux和Component中,我们的connect函数实现是这样的
然后,我们在具体的Component中使用封装好的这个connect函数,使用后的代码会变成这样
import React, { PureComponent } from 'react';
import {connect} from '../utils/connect';
import {
incAction,
addAction
} from '../store/actionCreators'
class Home extends PureComponent {
render() {
return (
<div>
<h1>Home</h1>
<h2>当前计数: {this.props.counter}</h2>
<button onClick={e => this.props.increment()}>+1</button>
<button onClick={e => this.props.addNumber(5)}>+5</button>
</div>
)
}
}
const mapStateToProps = state => ({
counter: state.counter
})
const mapDispatchToProps = dispatch => ({
increment() {
dispatch(incAction());
},
addNumber(num) {
dispatch(addAction(num));
}
})
export default connect(mapStateToProps, mapDispatchToProps)(Home);
可以看到,这个组件现在变得非常整洁,一些订阅,取消订阅,以及组件的生命周期的方法,全部都不需要了,只需要通过pros获取一些属性来显示即可,然后我们要实现类似的功能,只要定义对应的mapStateToProps和mapDispatchToProps函数即可,无需重复繁琐的代码,简直不要太爽
如此方便的操作,当然要分享出来,供全球的同行使用啊,于是想要进一步抽取,做到通用,现在有个问题是我们的connect函数里面会对store有强依赖,这样显然不行的,总不能让别人使用你的库,然后进到里面修改一下这一行,改成别人的store,这样显然行不通,于是,近一步对这个connect进行抽取后,变成了酱
创建一个StoreContext,进行数据共享
connect函数就变成了这样
import React, { PureComponent } from "react";
//创建一个StoreContext,进行传递数据
import { StoreContext } from './context';
export function connect(mapStateToProps, mapDispachToProp) {
return function enhanceHOC(WrappedComponent) {
class EnhanceComponent extends PureComponent {
constructor(props, context) {
super(props, context);
this.state = {
//通过context全局传递数据,做到store的解耦
storeState: mapStateToProps(context.getState())
}
}
componentDidMount() {
this.unsubscribe = this.context.subscribe(() => {
this.setState({
storeState: mapStateToProps(this.context.getState())
})
})
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return <WrappedComponent {...this.props}
{...mapStateToProps(this.context.getState())}
{...mapDispachToProp(this.context.dispatch)} />
}
}
EnhanceComponent.contextType = StoreContext;
return EnhanceComponent;
}
}
然后,我们在入口的地方,进行context的包裹,
解耦完成,现在我们的connect对业务逻辑的store没有任何依赖,是完全独立的一个方法,在我们准备封装自己的第三方库,很遗憾的告诉你,其实已经存在了这样的一个库,而且广为流传,就是著名的 react-redux
这么广为流传,肯定是有特别的原因,我们替换成这个库试一下,
react-redux实现:
入口代码变成酱紫
import React from 'react';
import ReactDOM from 'react-dom';
import store from './store';
// import { StoreContext } from './utils/context';
import { Provider } from 'react-redux';
import App from './App';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
发现唯一的区别就是Provider传参的时候value变成了store,其实进去源码看下来和我的实现是完全一样的,就是这里它更直接一点,参数名就叫store,也是为了更容易和我们自己的store对应。
然后我们在自己的组件里面,只需要把我们自己的connect方法换成react-redux的connect函数,惊奇的发现,其他的完全不需要任何改动,已经完成了对接,这不是巧了吗😝
到这里,肯定很想知道这个库这么多星,里面实现到底是怎么样的?是不是和我们的一样呢?
进去看了一通源码,大量的使用了hooks,这些相关的东西后面再抽空补充吧,但是追根溯源,其实整个设计思想,和我们是完全一致的,只是里面会加入一些性能优化方面的判断。
至此,redux基本上完成了整个设计思想的学习和记录!对其实现流程有了更加清晰的认识,后面使用起来更加知其然知其所以然了,更加有底气。
学无止境,在FullStack的路上持续乘风破浪,加油!!!