React 基础巩固(三十七)------自定义connect高阶组件
一、手撸一个自定义connect高阶组件
javascript
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,用于部分值修改时的浅层比较、更新state
this.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;
};
}
二、目前的问题
javascript
import store from "../store";
从这行代码可以看到,目前的connect直接引用了上级目录的store,过于依赖目前既定的store,这样不利于复用。假设另一个项目的store所在位置不在上级目录中,则会出现问题。
三、优化上面的丐版connect
为了让所有人都能使用,我们应该把这种"写死"的做法换成让开发者自己传入一个store:
-
构建一个StoreContext,用于创建Store的上下文(src/hoc/StoreContext.js):
javascriptimport { createContext } from "react"; export const StoreContext = createContext()
-
当我们在项目的index.js中引入connect时,引入并使用该上下文,让开发者手动传入当前的store(src/index.js):
javascriptimport 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):javascriptimport { 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,用于部分值修改时的浅层比较、更新state this.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 = StoreContext return NewComponent; }; }
-
最后,在hoc中构建index.js,将优化后的connect导出(hoc/index.js):
javascriptexport { StoreContext } from "./StoreContext"; export { connect } from "./connect";
-
在界面中使用现在优化后的connect:
javascriptimport 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);
-
查看效果,与之前效果一致: