react-redux及对应hook实现源码

79 阅读1分钟

step1: 先创建一个Context对象

const Context = react.createContext()

step2: 通过Provider组件传递context value

export function Provider({children,store}){
    return <Context.Provider value={store}>{children}</Context.Provider>
}

step3: 子组件接收context value

  • connect 是个hoc,接收组件作为参数,然后返回一个新组件
  • 新的组件上可以是由你选择映射state和dispatch
export const connect = (

    mapStateToProps = state=>state,

    mapDispatchToProps //数据类型是undefined,object,function

)=>WrappedComponent=>props=>{
    // 首先获取store
    const store = useContext(Context)
    // 获取state的函数
    const {getState, dispatch, subscribe} = store

    const stateProps = mapStateToProps(getState())

    let dispatchProps = {}

    if(typeof mapDispatchToProps ==="function"){

        dispatchProps = mapDispatchToProps(dispatch)

    }else if(typeof mapDispatchToProps === "object"){

        dispatchProps = bindActionCreators(mapDispatchToProps,dispatch)

    }else{

        dispatchProps = {dispatch}
    }
    return <WrappedComponent {...props} {...dispatchProps} {...stateProps} />
}
  • 组件更新
 const forceUpdate = useForceUpdate()
 useLayoutEffect(()=>{
     const unsubscribe = store.subscribe(()=>{
         forceUpdate();
     })
     return ()=>{
         if(unsubscribe){
             unsubscribe()
         }
     }
 },[store])

函数组件怎么实现类似forceUpdate的方法

 // 定义
 function useForceUpdate(){
     const [state,setState] = useState(0)
     const update = useCallback(()=>{
        setState(prev=>prev+1)
     },[])
     return update
 }
// 使用
const forceUpdate = useForceUpdate()

这里用useLayoutEffect而不是useEffect的原因

  • useEffect在组件更新完之后有延迟,有可能会漏掉一些订阅
  • useLayoutEffect是渲染之后同步执行,useEffect是异步批量执行

react-redux Hook

  • useSelector(({count})=>count): 拿到整个state数据
// 实现
export fnuction useSelector(selector){
    const store = useContext(Context)
    const {getState} = store
    const selectedState = selector(getState())
    return selectedState
}
  • useDispatch()
// 实现
export function useDispatch(){
    const store = useStore()
    // ... 加上上边订阅更新的代码
    return store.dispatch
}