React 基础巩固(三十七)——自定义connect高阶组件
一、手撸一个自定义connect高阶组件
import { PureComponent } from "react";
import store from "../store";/*** connect的参数:* 参数一: 函数* 参数二: 函数* 返回值: 函数*/
export default function connect(mapStateToProps, mapDispatchToProps) {// 返回一个高阶组件,本质也是函数return function (WrapperComponent) {class NewComponent extends PureComponent {constructor(props) {super(props);// 将接收到的mapStateToProps赋给state,用于部分值修改时的浅层比较、更新statethis.state = mapStateToProps(store.getState());}componentDidMount() {this.unsubscribe = store.subscribe(() => {this.setState(mapStateToProps(store.getState()));});}componentWillUnmount() {this.unsubscribe();}render() {// 将接收到的mapStateToProps、mapDispatchToProps传入要返回的新组件中const stateObj = mapStateToProps(store.getState());const dispatchObj = mapDispatchToProps(store.dispatch);return (<WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />);}}return NewComponent;};
}
二、目前的问题
import store from "../store";
从这行代码可以看到,目前的connect直接引用了上级目录的store,过于依赖目前既定的store,这样不利于复用。假设另一个项目的store所在位置不在上级目录中,则会出现问题。
三、优化上面的丐版connect
为了让所有人都能使用,我们应该把这种“写死”的做法换成让开发者自己传入一个store:
-
构建一个StoreContext,用于创建Store的上下文(src/hoc/StoreContext.js):
import { createContext } from "react";export const StoreContext = createContext()
-
当我们在项目的index.js中引入connect时,引入并使用该上下文,让开发者手动传入当前的store(src/index.js):
import React from "react"; import ReactDOM from "react-dom/client"; import { Provider } from "react-redux"; import { StoreContext } from "./hoc"; import App from "./App"; import store from "./store";const root = ReactDOM.createRoot(document.getElementById("root")); root.render(// <React.StrictMode><Provider store={store}><StoreContext.Provider value={store}><App /></StoreContext.Provider></Provider>// </React.StrictMode> );
-
在connect中,通过 contextType 共享从 Provider 中传入的 store 变量,将原来直接引用的
store
替换成this.context
(hoc/connect.js):import { PureComponent } from "react"; import { StoreContext } from "./StoreContext";/*** connect的参数:* 参数一: 函数* 参数二: 函数* 返回值: 函数*/ export function connect(mapStateToProps, mapDispatchToProps) {// 返回一个高阶组件,本质也是函数return function (WrapperComponent) {class NewComponent extends PureComponent {constructor(props, context) {super(props);// 将接收到的mapStateToProps赋给state,用于部分值修改时的浅层比较、更新statethis.state = mapStateToProps(context.getState());}componentDidMount() {this.unsubscribe = this.context.subscribe(() => {this.setState(mapStateToProps(this.context.getState()));});}componentWillUnmount() {this.unsubscribe();}render() {// 将接收到的mapStateToProps、mapDispatchToProps传入要返回的新组件中const stateObj = mapStateToProps(this.context.getState());const dispatchObj = mapDispatchToProps(this.context.dispatch);return (<WrapperComponent {...this.props} {...stateObj} {...dispatchObj} />);}}// 在类组件中,通过 contextType 共享store变量NewComponent.contextType = StoreContextreturn NewComponent;}; }
-
最后,在hoc中构建index.js,将优化后的connect导出(hoc/index.js):
export { StoreContext } from "./StoreContext"; export { connect } from "./connect";
-
在界面中使用现在优化后的connect:
import React, { PureComponent } from "react"; import { connect } from "../hoc"; import { addNumber } from "../store/features/counter";export class About extends PureComponent {render() {const { counter } = this.props;return (<div><h2>About Counter: {counter}</h2></div>);} }const mapStateToProps = (state) => ({counter: state.counter.counter, });const mapDispatchToProps = (dispatch) => ({addNumber(num) {dispatch(addNumber(num));}, });export default connect(mapStateToProps, mapDispatchToProps)(About);
-
查看效果,与之前效果一致: