• useReducer+createContext真的可以代替Redux吗?


    在这里插入图片描22述

    您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~

    前言

    最近看到很多采用useReducer + createContext 实现一个简易的redux的方案,今天亲自试了一下,发现还是会有一些区别的。

    采用react-redux实现

    这里使用react-redux 实现一个简单的状态管理例子。

    App.jsx根组件

    import React from 'react';
    import { Button } from './Button';
    import { createStore } from 'redux';
    import { Provider } from 'react-redux';
    import A from './a';
    
    export default function ButtonDemo1() {
      const reducer = (state, action) => {
        const { name, theme } = state;
        switch (action.type) {
          case 'UPDATENAME':
            return {
              ...state,
              name: `${name} + 1`,
            };
          case 'UPDATETHEME':
            return {
              ...state,
              theme: theme === 'dark' ? 'light' : 'dark',
            };
          default:
            return state;
        }
      };
      const store = createStore(reducer, {
        name: 'fx',
        theme: 'dark',
      });
    
      return (
        <Provider store={store}>
          <div>
            <Button />
            <A />
          </div>
        </Provider>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    A组件用于dispatch和接收store

    A.jsx

    import React, { useContext } from 'react';
    import { useSelector, useDispatch } from 'react-redux';
    import { reduxContent } from './index1';
    
    export default function A() {
      const dispatch = useDispatch();
    
      return (
        <div onClick={() => dispatch({ type: 'UPDATENAME' })}>
          {useSelector((state) => state.name)}
        </div>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    效果如图:

    在这里插入图片描述

    可以看到,Button组件未使用redux store,因此正常渲染了一次。

    采用react hooks模拟redux实现

    这里采用useReducer + createContext 模拟实现一个redux

    App.jsx

    import React, { useReducer, createContext } from 'react';
    import { Button } from 'concis';
    import A from './a';
    
    export const reduxContent = createContext({});
    
    export default function ButtonDemo1() {
      const reducer = (state, action) => {
        const { name, theme } = state;
        switch (action.type) {
          case 'UPDATENAME':
            return {
              ...state,
              name: `${name} + 1`,
            };
          case 'UPDATETHEME':
            return {
              ...state,
              theme: theme === 'dark' ? 'light' : 'dark',
            };
          default:
            return state;
        }
      };
    
      const [redux, dispatch] = useReducer(reducer, {
        name: 'fx',
        theme: 'dark',
      });
    
      return (
        <reduxContent.Provider value={{ redux, dispatch }}>
            <Button />
            <A />
        </reduxContent.Provider>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    A.jsx

    import React, { useContext } from 'react';
    import { reduxContent } from './index1';
    
    export default function A() {
      const { redux, dispatch } = useContext(reduxContent);
    
      return (
        <div onClick={() => dispatch({ type: 'UPDATENAME' })}>
          {redux.name}
        </div>
      );
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    同样,子组件也可以对store中的状态进行getdispatch,但是会出现这样的问题:

    在这里插入图片描述

    可以看到,Button组件并没有使用store中的内容,但是会随着A组件一起跟着重新渲染,原因其实就是采用这种方式store是存储在根组件的,根组件状态发生了变化(useReducer),子组件跟着一起重新渲染了,因此解决这个问题的思路其实和解决常规的子组件没变化一起被更新的思路是一样的。

    可以采用 useMemo限制 + memo 浅比较。

    因此只需要在App.jsx中这样修改:

     const renderButton = React.useMemo(() => {
        return <Button />;
      }, []);
    
      return (
        <reduxContent.Provider value={{ redux, dispatch }}>
          <div style={{ display: 'flex', flexWrap: 'wrap' }}>
            {renderButton}
            <A />
          </div>
        </reduxContent.Provider>
      );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Button.jsx

    const Button = (props) => {
        ......
    })
    
    export default React.memo(Button);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    异步action

    同样,如果需要异步dispatch的话,简单的场景其实单纯使用异步操作就可以完成,但是在复杂的场景下很难对于异步流进行管理和维护,这时就需要借助redux中间件了,类似redux-thunkredux-saga,而这也是使用hooks无法实现的,无法处理副作用,拦截action去更好的reducer

    总结

    当然,并不是说采用react hooks所实现的状态管理方式没有好处,这样可以更加贴合react原生,采用react自身所提供的hook,并且可以减少项目中的redux各种实例、减少代码体积,对于小型项目或是不需要很多全局状态的项目,这种方式确实是不错的选择。但是redux仍然是大型项目中最可靠的保障存在。

  • 相关阅读:
    Nvm 安装
    Ubuntu 20.04.5安装无线网卡RTL8821CE驱动
    你知道有哪些类型的接口吗?
    【TCP】滑动窗口、流量控制 以及拥塞控制
    【K8S系列】第十讲:Knative 简介
    Java面试常见题:手写单向链表的实现思路
    阿里云视频点播+项目实战
    手写RPC框架-注册中心实现
    React Router 6 + Ant Design:构建基于角色的动态路由和菜单
    买卖股票系列问题——DP
  • 原文地址:https://blog.csdn.net/m0_46995864/article/details/127662229