react-redux的功能如下:
- Provider 为后代组件提供store
- connect 为组件提供数据和变更方法
- 数据变化时自动更新组件
了解react-redux的功能移步这里
下面我们开始实现react-redux的几个功能:
//my-react-redux.js
import React, {useContext, useReducer, useLayoutEffect} from "react";
const Context = React.createContext(); //创建全局state
//1. 实现Provider组件:为后代组件提供store
export function Provider({store, children}) {
return <Context.Provider value={store}>{children}</Context.Provider>;
}
//2. 实现connect方法:为组件提供数据和变更方法
export const connect = (
mapStateToProps = state => state, //默认是一个方法
mapDispatchToProps
) => WrappendComponent => props => { // WrappendComponent 是受益的组件
const store = useContext(Context)
const {dispatch, getState, subscribe} = store
const stateProps = mapStateToProps(getState()) //获取需要的state
let dispatchProps = { dispatch }
// 让函数强制更新的方法
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
//mapDispatchToProps可以是function与object
if (typeof mapDispatchToProps === "function") {
dispatchProps = mapDispatchToProps(dispatch)
} else if (typeof mapDispatchToProps === "object") {
dispatchProps = bindActionCreators(mapDispatchToProps, dispatch)
}
//3. 实现同步副作用 state改变时自动更新组件
useLayoutEffect(() => {
//订阅state改变
const unsubscribe = subscribe(() => {
forceUpdate() //强制刷新
})
//返回一个 注销订阅函数
return () => {
if (unsubscribe) {
unsubscribe()
}
}
}, [store])
//将state与dispatch映射到组件内 完成connect方法的任务
return <WrappendComponent {...props} {...stateProps} {...dispatchProps} />
}
function bindActionCreator(creator, dispatch) {
return (...args) => dispatch(creator(...args))
}
//@connect()装饰器内需要一个bindActionCreators方法
//作用是结构creators,只需写type即可自动实现dispatch方法,让creators写的更简单
function bindActionCreators(creators, dispatch) {
const obj = {};
for (let key in creators) {
obj[key] = bindActionCreator(creators[key], dispatch)
}
return obj
}
了解@connect()装饰器移步这里
react-redux hooks API及实现
- useSelector 获取store state
- useDispatch 获取dispatch
import React, {useCallback} from "react"
import {useSelector, useDispatch} from "react-redux"
export default function ReactReduxHookPage({value}) {
const dispatch = useDispatch()
const add = useCallback(() => {
dispatch({type: "ADD"})
}, [])
const count = useSelector(({count}) => count);
return (
<div>
<h3>{count}</h3>
<button onClick={add}>add</button>
</div>
)
}
实现hooks API:
export function useSelector(selector) {
const store = useStore()
const {getState, subscribe} = store
const selectedState = selector(getState())
const [ignored, forceUpdate] = useReducer(x => x + 1, 0);
useLayoutEffect(() => {
const unsubscribe = subscribe(() => {
forceUpdate()
});
return () => {
if (unsubscribe) {
unsubscribe()
}
};
}, [store])
return selectedState
}
export function useDispatch() {
const store = useStore()
return store.dispatch
}
export function useStore() {
const store = useContext(Context)
return store
}