V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
FaiChou
V2EX  ›  React

React 中, 为什么要用 Context? 直接使用全局变量不是更方便吗?

  •  1
     
  •   FaiChou ·
    FaiChou · 2022-05-07 14:19:05 +08:00 · 3800 次点击
    这是一个创建于 960 天前的主题,其中的信息可能已经有所发展或是发生改变。

    未免有点标题党, 且先看我写的这个 demo:

    import React from 'react';
    let obj = null;
    let listener = null;
    function getObj() { return obj; }
    function setListener(l) { listener = l; }
    function setObj(o) {
      obj = o;
      if (listener) { listener(o); }
    }
    function useObj() {
      const [, forceUpdate] = React.useReducer(x=>x+1,0);
      React.useEffect(() => {
        setListener(() => forceUpdate())
      }, [])
      return getObj();
    }
    function Component1() {
      return (
        <div>
          <button onClick={() => {setObj(Math.random())}}>setrandom</button>
        </div>
      )
    }
    function Component2() {
      const o = useObj();
      return (
        <div>
          {o}
        </div>
      )
    }
    export default function App() {
      return (
        <div>
          <Component1 />
          <Component2 />
        </div>
      );
    }
    

    先说下 Context 一个缺点, 当数据改变时, 在 Context.Provider 下的节点都会重新执行, 这样很多不用其数据的节点也会被打扰, 昨天的帖子讨论过: 用 Context+Hooks 替代 Redux. 认真看了下大家的评论, 要么是用 memo 来固定住不想被打扰的组件, 要么使用一个订阅模式来刷新并通知.

    与其这么麻烦, 不如直接用上面代码中的方法. 毕竟 Context 可以看作是一个全局的数据, 任意节点想使用这个数据时候, 还是需要 import 这个 Context.

    上面代码和 redux 很像, 有一个 listener, 但 redux 需要靠 connect 绑定组件来订阅刷新, connect 利用了 Context.Provider+订阅+useSyncExternalStore 这个 API 来实现的.

    还不如直接简简单单使用上面这种方式, 当节点使用全局数据时候, 使用自定义 hook 插一个 listener 进去, 当数据变动, 进行 forceUpdate. 这样也会避免了牵一发动全身的全部刷新, 只有使用 useObj() 的组件才会被刷新.

    ps. 我写的项目少, 只是看文档时候产生的一点想法. 求大佬指正

    12 条回复    2022-05-08 22:42:42 +08:00
    codefever
        1
    codefever  
       2022-05-07 14:24:46 +08:00
    没有 Context 的时候父组件向子组件传递 props 属性只能在组件树上自上而下进行传递,但是有些属性并不是组件树的每层节点都有相同的需求,这样我们再逐层传递 props 就显得代码很繁琐笨重。
    noe132
        2
    noe132  
       2022-05-07 14:31:13 +08:00 via Android
    假设你写了个第三方库,一些组件依赖一些父组件的状态,但它们不是直接父子关系,而且可能被用户随意组合,这时候 context 就是非常好的工具
    XCFOX
        3
    XCFOX  
       2022-05-07 15:08:00 +08:00
    你说的很对,用全局变量再加上 render-optimized 是个不错的方案,valtio 就是这么干的
    https://github.com/pmndrs/valtio
    XCFOX
        4
    XCFOX  
       2022-05-07 15:15:39 +08:00
    转念一想,其实大家早就看不惯 React 这种函数式的贪婪更新机制。
    所以后来的 Vue3 、Svelte 、Solid 都是监听变化按需更新,可以说它们比 React 更 reactive ,性能比 React 好不少,也没有到处 useMemo 、memo 的烦恼。
    vue3 是我觉得最舒服的,reactive 对象可以作为全局变量存在于组件之外,这样极大方便了组件间通信。不过话说回来,全局变量还是得小心地用,不然会有内存驻留的风险。
    yyfearth
        5
    yyfearth  
       2022-05-07 15:50:31 +08:00
    @FaiChou 用 Context 而不是 Global
    如果你是写可复用组件 而不是你例子里面这种单例组件 你就完全没办法用 global

    用 Context 本来就是要避免平凡的更新里面的东西 如果只需要取值 不需要刷新 可以用 Context 传递 Ref

    我写全局单例组件的时候 一般也用 Global 的多 但是可复用组件 就不能用 Global 了
    yyfearth
        6
    yyfearth  
       2022-05-07 15:53:00 +08:00
    @XCFOX React 的 virtual dom 就是那个时代的产物
    那个时代没有现在这么强大的工具链可以做到 Svelte 那样
    要兼容浏览器也没有 Proxy 来做到原生 reactive

    有了强大的编译器和现代浏览器 确实完全不需要 virtual dom 了
    rioshikelong121
        7
    rioshikelong121  
       2022-05-07 15:55:22 +08:00
    redux 也支持 hook API (useDispatch, useSelector) 啊,也不需要显式使用 connect ,我觉得这就够了。
    比你个方案好的是支持 middleware 这样的机制。
    FaiChou
        8
    FaiChou  
    OP
       2022-05-07 15:59:25 +08:00
    @rioshikelong121 的确, 代替的是 react-redux 而没有代替 redux, 因为 redux 支持中间件.
    gouflv
        9
    gouflv  
       2022-05-07 17:03:17 +08:00 via iPhone
    context 是可以分层级管理的,作用域更小,同时还具有生命周期
    bojue
        10
    bojue  
       2022-05-07 23:50:05 +08:00
    @codefever Content 能避免逐层传递呀
    chenliangngng
        11
    chenliangngng  
       2022-05-08 12:30:54 +08:00
    登录状态我这里就是全局变量+闭包控制的,没有用 redux 也没有用 hooks 也没有用 context 。原因很简单,我希望这个状态和 react 生态解耦。一些其他需要用到登录状态的中间件,我没必要用 react 的方式去写

    看你业务场景吧,适合就是好的。微前端场景,完全可以全局变量+闭包;全局状态的场景,可以用 redux 也可以用 context ,而且尽量减少全局状态,尽量减少状态,少用状态管理,因为这会让项目指数级复杂化;一个模块的状态,用 context 是最好的,在模块最顶级文件使用,可以保证其他模块不去用,不需要 props 传参
    shiye515
        12
    shiye515  
       2022-05-08 22:42:42 +08:00
    自己一个人开发的时候随便,很多人一起合作的时候根本找不到全局变量是被谁在什么时候改了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2514 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:35 · PVG 23:35 · LAX 07:35 · JFK 10:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.