最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • react-hook的存在是否可以完全替代redux

    正文概述 掘金(小小荧)   2021-02-09   714

    前文

    react-redux主要提供的功能是将redux和react的组件关联起来。使用提供的connect方法可以使得任意一个react组件获取到全局的store。 实现方法是将store存放于由provider提供的context上,在调用connect时, 就可将组件的props替换, 让其可以访问到定制化的数据或者方法。

    目标

    本文尝试使用react-hook来替代react-redux的基本功能。

    react-redux的特点:

    • 全局维护一个store
    • 任何组件都可以获得store,最好props可以定制(mapStateProps)
    • 提供action的派发能力(mapStateDispatch)

    useReducer

    先看一下他的内置能力是什么,官网的案例会可我们一些启示能力(也是useState的替代方案)。

    const initialState = {count: 0};
    
    function reducer(state, action) {
      switch (action.type) {
        case 'increment':
          return {count: state.count + 1};
        case 'decrement':
          return {count: state.count - 1};
        default:
          throw new Error();
      }
    }
    
    function Counter() {
      const [state, dispatch] = useReducer(reducer, initialState);
      return (
        <>
          Count: {state.count}
          <button onClick={() => dispatch({type: 'decrement'})}>-</button>
          <button onClick={() => dispatch({type: 'increment'})}>+</button>
        </>
      );
    }
    

    这么看来实际上hook拥有了可以使用redux的机制,状态的派发改变的action,单向的数据流。但是hook不会状态共享,也就是每次useReducer保持是数据状态都是独立的。比如下面这个例子:

    function CountWrapper() {
        return (
            <section>
                <Counter initialCount={1}/>
                <Counter initialCount={1}/>
            </setion>
            )
    }
    

    两个Count组件内部的数据是独立的,无法互相影响,状态管理也就是无法说起。本身useReducer就是使用useState进行封装实现的。

    function useReducer(reducer, initialState) {
      const [state, setState] = useState(initialState);
     
      function dispatch(action) {
        const nextState = reducer(state, action);
        setState(nextState);
      }
     
      return [state, dispatch];
    }
    

    StorePriovider

    useReducer实际上不能替代react-redux的全局共享机制,按照react-redux的实现方式来看,是因为提供了一个Provider,使用context的方式来做的,这里使用useContext来弥补这个问题

    它本身接收的是一个React.createContext的上下文对象,当provider更新时,会传入store更新时,useContext就可以返回最新的值

    import {createContext, useContext} from 'react';
     
    const context = createContext(null);
    export const StoreProvider = context.provider;
     
    const store = useContext(context);
    
    

    useDispatch

    到这里我们提供了一个根组件来store, 当store更新时,我们也可以利用useContext也可以拿到最新的值。这个时候暴露出一个hook来返回store上的dispatch即可派发action,来更改state。

    export function useDispatch() {
      const store = useContext(Context);
      return store.dispatch;
    }
    

    useStoreState

    接下来着眼于组件拿到store上数据的问题,这个其实也很简单,我们都把store拿到了,编写一个自定义hook调用了store.getStore()即可拿到全局的状态。

    export function useStoreState(mapState){
        const store = useContext(context);
        return mapState(store.getStore());
    }
    

    这里虽然是把状态拿到了,但忽略了一个非常重要的问题, 当store上的数据变化时,如何通知组件再次获取新的数据。当store变化过后,并没有和视图关联起来。另一个问题是没有关注mapState变化的情况。 针对第一个问题,我们可以利用useEffect这个内置hook,在组件mount时完成在store上的订阅,并在unmont的时候取消订阅。 mapState的变更可以使用useState来监听, 每次有变更时就执行向对应的setter方法。代码如下

    export function useStoreState(mapState) {
        const store = useContext(context);
     
        const mapStateFn = () => mapState(store.getState());
     
        const [mappedState, setMappedState] = useState(() => mapStateFn());
     
        // If the store or mapState change, rerun mapState
        const [prevStore, setPrevStore] = useState(store);
        const [prevMapState, setPrevMapState] = useState(() => mapState);
        if (prevStore !== store || prevMapState !== mapState) {
            setPrevStore(store);
            setPrevMapState(() => mapState);
            setMappedState(mapStateFn());
        }
     
        const lastRenderedMappedState = useRef();
        // Set the last mapped state after rendering.
        useEffect(() => {
            lastRenderedMappedState.current = mappedState;
        });
     
        useEffect(
            () => {
                // Run the mapState callback and if the result has changed, make the
                // component re-render with the new state.
                const checkForUpdates = () => {
                    const newMappedState = mapStateFn();
                    if (!shallowEqual(newMappedState, lastRenderedMappedState.current)) {
                        setMappedState(newMappedState);
                    }
                };
                            
                // Pull data from the store on first render.
                checkForUpdates();
     
                // Subscribe to the store to be notified of subsequent changes.
                const unsubscribe = store.subscribe(checkForUpdates);
     
                // The return value of useEffect will be called when unmounting, so
                // we use it to unsubscribe from the store.
                return unsubscribe;
            },
            [store, mapState],
        );
        return mappedState
    }
    

    ok,从这里来看,react-hook的确可以替代react-redux,自己通过简单的封装基本完成了react-redux大部分的功能,但是,换一个场景来说,在大型web服务场景中,react-redux的优化策略和考虑的场景,包括和第三方的框架继承,例如immutable-js, redux-thunk,redux-saga。这样的优化方案,如果你需要自己做集成实现可能成本较高,同时无法考虑全面和对大型web服务的有精准的定位。而且最新的react生态产生的react-server-compoents服务端组件渲染技术,将会减少大量的数据渲染,数据处理的成本,也会在数据维护和性能优化做到一个质的飞跃。但是,中小型的web项目还需要使用react-redux么?实际上不太需要,绝大数据的中小型的web系统,没有太多的复杂交互场景和异步问题,可能只需要借助react-dom和axios就可以完成整个项目的开发了。


    起源地下载网 » react-hook的存在是否可以完全替代redux

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元