React Redux
💡 React 使用 redux 的时候,每一个类组件,都要在生命周期当中去订阅,取消订阅,去绑定dispatch,获取状态。所以可以使用 HOC 统一一优化,React-redux 当中提供了一个 connect 高阶函数和 Provider组件 来优化这些统一的操作。提供 useSelector, useDispatch Hook 来供函数式组件消费 store 当中的数据。
实现 React Redux
Provider 组件
💡 将 redux 当中的 store,通过 React Context 全局共享。
export const ReactReduxContext = React.createContext(null);
function Provider(props) {
return (
<ReactReduxContext.Provider value={{ store: props.store }}>
{props.children}
</ReactReduxContext.Provider>
);
}
Connect 函数
💡 通过向 connect 函数 当中传递 mapStateToProps | mapDispatchToProps, 将 redux 中的 store 和 dispatch 通过 HOC 映射到 组件的 props 当中。
类组件版
function connect(mapStateToProps, mapDispatchToProps) {
return function (OldComponent) {
return class extends React.Component {
static contextType = ReactReduxContext;
constructor(props, context) {
super(props);
const { store } = context;
const { getState, subscribe, dispatch } = store;
this.state = mapStateToProps(getState());
this.unsubscribe = subscribe(() => {
this.setState(mapStateToProps(getState()));
});
let dispatchProps;
//把dispatch映射为属性
if (typeof mapDispatchToProps === "function") {
dispatchProps = mapDispatchToProps(dispatch);
} else if (typeof mapDispatchToProps === "object") {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
} else {
dispatchProps = { dispatch };
}
this.dispatchProps = dispatchProps;
}
componentWillUnmount() {
this.unsubscribe();
}
render() {
return (
<OldComponent
{...this.props}
{...this.state}
{...this.dispatchProps}
/>
);
}
};
};
}
函数组件版
// hook 实现 connect 函数
function connect2(mapStateToProps, mapDispatchToProps) {
return function (OldComponent) {
return function (props) {
//返回的Counter1组件
const { store } = useContext(ReactReduxContext);
const { getState, dispatch, subscribe } = store;
const prevState = getState();
//把状态映射为属性
const stateProps = useMemo(() => mapStateToProps(prevState), [prevState]);
let dispatchProps = useMemo(() => {
console.log("dispatchProps render");
let dispatchProps;
if (typeof mapDispatchToProps === "function") {
dispatchProps = mapDispatchToProps(dispatch);
} else if (typeof mapDispatchToProps === "object") {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch);
} else {
dispatchProps = { dispatch };
}
return dispatchProps;
}, [dispatch]);
//把dispatch映射为属性
//forceUpdate模拟的类组件的强制刷新方法 React 18之后可以新的API useSyncExternalStore
const [, forceUpdate] = useReducer((x) => x + 1, 0);
useLayoutEffect(() => {
//如果仓库里的状态发生变化之后,会就执行forceUpdate
return subscribe(forceUpdate);
}, [subscribe]);
return <OldComponent {...props} {...stateProps} {...dispatchProps} />;
};
};
}
useSelector Hook
通过 context 拿到共享的 store,通过传递的 selector 来计算 新的 state 值。
function useSelectorWithStore(selector, store) {
let { subscribe, getState } = store;
const [, forceUpdate] = React.useReducer((x) => x + 1, 0);
let state = getState();
let selectedState = selector(state);
React.useLayoutEffect(() => {
//其实这个订阅只会执行一次就可以了
return subscribe(forceUpdate);
}, [subscribe]);
return selectedState;
}
function useSelector(selector) {
const { store } = React.useContext(ReactReduxContext);
const selectedState = useSelectorWithStore(selector, store);
return selectedState;
}
useDispatch Hook
通过 context 拿到共享的 dispatch
function useDispatch() {
const { store } = React.useContext(ReactReduxContext);
return store.dispatch;
}