react-redux的功能如下:
- Provider 为后代组件提供store
- connect 为组件提供数据和变更方法
- 数据变化时自动更新组件
下面我们开始实现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 }
实现hooks API:
- useSelector 获取store state
- useDispatch 获取dispatch
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 }
使用:
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> ) }