从0实现react-redux
上一节我们介绍了redux的用法和源码的实现,使用时都要在componentDidMount中订阅,在组件销毁时取消订阅,还需要用boundActionCreators方法对action进行绑定,那么怎么简化这些东西呢?
接下来我们来实现另外一个库react-redux,主要作用就是用来简化在react中使用redux的流程。
我们先用原生的react-redux来改造我们的组件Counter1
- 引入
Provider将store做为属性传给Provider,这样Provider包裹的所有组件都可以拿到store,而不用在每个组件内都引入store
import React from "react";
import ReactDOM from "react-dom";
+import { Provider } from "react-redux";
import store from "./store";
import Counter1 from "./components/Counter1";
import Counter2 from "./components/Counter2";
ReactDOM.render(
+ <Provider store={store}>
<Counter1 />
<Counter2 />
+ </Provider>,
document.getElementById("root")
);
- 引入
connect方法,用来将store里的state、定义的action和组件进行绑定,注入组件的props中。
import React, { Component } from "react";
import actions from "../store/actions/counter1";
// 引入connect方法
import { connect } from "react-redux";
class Counter1 extends Component {
render() {
// 连接后的组件,可以在props中取到 属性
let { number, add1, minus1 } = this.props;
return (
<div>
<p>{number}</p>
<button onClick={add1}>+</button>
<button onClick={minus1}>-</button>
<button onClick={() => setTimeout(() => add1(), 1000)}>1秒后加1</button>
</div>
);
}
}
let mapStateToProps = (state) => state.counter1;
// 将组件和状态、action进行连接
export default connect(mapStateToProps, actions)(Counter1);
是不是简单多了,这样就不用在每个组件中引入
store,绑定action,定义订阅和取消订阅的函数了,那内部是怎么实现的呢?
实现Provider组件
Provider就是对React context的一层封装,将store作为value提供给所有子组件
import React from "react";
import ReactReduxContext from "./ReactReduxContext";
export default function (props) {
return (
<ReactReduxContext.Provider value={{ store: props.store }}>
{props.children}
</ReactReduxContext.Provider>
);
}
- 创建
ReactReduxContext对象
import React from "react";
export const ReactReduxContext = React.createContext(null);
export default ReactReduxContext;
类组件 实现connect方法
connect方法是一个高阶函数,传入mapStateToProps、actions,返回一个高阶组件,在高阶组件中对原有的组件进行封装,注入新的属性和方法。- 1、首先通过
context获取 Provider组件提供的store。 - 2、在componentDidMount中用获取的
store订阅state的状态,当state发生变化时,调用forceUpdate方法重新渲染组件 - 3、在
componentWillUnmount组件将要卸载时取消订阅 - 4、在
render中拿到store里的state, 再通过传入的mapStateToProps方法获取到组件依赖的状态stateProps, 将这些状态作为props属性注入到OldComponent组件中 - 5、通过
bindActionCreators方法,将 传入的actions和store里的dispatch方法进行绑定,绑定的结果dispatchProps也作为props属性注入到OldComponent组件中
- 1、首先通过
- 这样在
OldComponent中就可以通过props属性拿到store里的state和绑定后的action进行使用 - 当调用
action时,会向store中派发事件,通过reducer函数更新状态,再通过组件订阅的方法更新组件状态
import React, { useContext } from "react";
import { bindActionCreators } from "../redux";
import ReactReduxContext from "./ReactReduxContext";
// 类组件实现
function connect(mapStateToProps, actions) {
return function (OldComponent) {
return class NewComponent extends React.Component {
// 获取Provider提供的store
static contextType = ReactReduxContext;
constructor(props, context) {
super(props, context);
}
componentDidMount() {
// 订阅状态更改,重新渲染组件
this.unsubscribe = this.context.store.subscribe(() =>
this.forceUpdate()
);
}
componentWillUnmount() {
// 组件卸载,取消订阅
this.unsubscribe();
}
render() {
// 获取store中的状态
const { store } = this.context;
const state = store.getState();
// 获取组件依赖的状态
const stateProps = mapStateToProps(state);
// 绑定action
const dispatchProps = bindActionCreators(actions, store.dispatch);
return (
<OldComponent {...this.props} {...stateProps} {...dispatchProps} />
);
}
};
};
}
export default connect;
代码地址
如果这篇文章对你有帮助,请帮我点个赞吧