这一节美容不是很难次要是 react-reudx 的外围局部,这部分其实 redux 也有,就是 Provider、connect、bindActionCreators 等几个罕用的 API 的实现。
间接上外围代码
import React, {useLayoutEffect, useReducer, useCallback, useContext} from 'react';
const useForceUpdata = () => {const [_, forceUpdata] = useReducer(x => x + 1, 0,);
const upDate = useCallback(() => forceUpdata(), [])
return upDate
};
const Cunsumer = ({store}, mapStateToProps, mapDispatchToProps, WarppedComponent, props) => {
// stateProps 其实就是以所有 state 为参数,mapStateProps 执行的后果
const forceUpdata = useForceUpdata()
const stateProps = mapStateToProps(store.getStore());
// dispatchProps 须要麻烦一点因为会有两种状况
// 第一种 mapDispatchProps 是函数
// 第二种 mapDispatchProps 是对象
let dispatchProps = {dispatch: store.dispacth};
// 这里补充一下最精确的判断数据类型的办法:Object.prototype.toString.call(mapDispatchProps)
if (typeof mapDispatchToProps === "function") {dispatchProps = mapDispatchToProps(store.dispach)
}
else if (typeof mapDispatchToProps === "object") {dispatchProps = bindActionCreators(mapDispatchToProps, store.dispach)
}
// * 重点 这里是必须要写订阅的不然咱们代码跑起来不会报错然而页面也不会刷新,// * redux 只是一个状态存储库,不具备主动刷新页面的性能,须要咱们自行编写订阅代码
// * 这里应用 useLayoutEffect 而不是 useEffect, 是因为 useLayoutEffect 在 dom 变更后就开始同步执行,而 useEffect 有提早
useLayoutEffect(() => {const unsubscribe = store.subscribe(() => {forceUpdata()
})
return () => {unsubscribe()
};
}, [store])
return <WarppedComponent {...props} {...stateProps} {...dispatchProps} />
};
// 创立 Context
const Context = React.createContext();
// 导出 Provider 组件
export const Provider = ({store, children}) => {return <Context.Provider value={store}>{children}</Context.Provider>
};
export const connect = ({mapStateToProps, mapDispatchToProps}) => WarppedComponent => props => {
// 子孙组件生产父级传下来的 value
return <Context.Cunsumer>
{(value) => Cunsumer(value, mapStateToProps, mapDispatchToProps, WarppedComponent, props)}
</Context.Cunsumer>
}
export const bindActionCreators = (data, dispacth) => {let obj = {}
for (const key in data) {obj[key] = dispacth((...arg) => obj[key](...arg))
}
return obj
};
export const useDispatch = () => {const store = useContext(Context)
// 间接返回 store 中的 dispatch 即可
return store.dispatch
}
export const useSelector = (selctor) => {const store = useContext(Context)
// 这里一样是要订阅一下不然页面不会更新
useLayoutEffect(() => {const unsubscribe = store.subscribe(() => {forceUpdata()
})
return () => unsubscribe()
}, [store])
return selctor(store.getStore())
}
以上便是几个罕用 API 的根本实现.
重点说一下
- 咱们这里写的 useSelector 不能够反复调用,不然会产生反复订阅的成果,影响性能。
- 订阅时应用 useLayoutEffect 而不是 useEffect, 是因为 useLayoutEffect 在 dom 变更后就开始同步执行,而 useEffect 有提早,详情请移步