未免有点标题党, 且先看我写的这个 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. 我写的项目少, 只是看文档时候产生的一点想法. 求大佬指正
1
codefever 2022-05-07 14:24:46 +08:00
没有 Context 的时候父组件向子组件传递 props 属性只能在组件树上自上而下进行传递,但是有些属性并不是组件树的每层节点都有相同的需求,这样我们再逐层传递 props 就显得代码很繁琐笨重。
|
2
noe132 2022-05-07 14:31:13 +08:00 via Android
假设你写了个第三方库,一些组件依赖一些父组件的状态,但它们不是直接父子关系,而且可能被用户随意组合,这时候 context 就是非常好的工具
|
3
XCFOX 2022-05-07 15:08:00 +08:00
你说的很对,用全局变量再加上 render-optimized 是个不错的方案,valtio 就是这么干的
https://github.com/pmndrs/valtio |
4
XCFOX 2022-05-07 15:15:39 +08:00
转念一想,其实大家早就看不惯 React 这种函数式的贪婪更新机制。
所以后来的 Vue3 、Svelte 、Solid 都是监听变化按需更新,可以说它们比 React 更 reactive ,性能比 React 好不少,也没有到处 useMemo 、memo 的烦恼。 vue3 是我觉得最舒服的,reactive 对象可以作为全局变量存在于组件之外,这样极大方便了组件间通信。不过话说回来,全局变量还是得小心地用,不然会有内存驻留的风险。 |
5
yyfearth 2022-05-07 15:50:31 +08:00
@FaiChou 用 Context 而不是 Global
如果你是写可复用组件 而不是你例子里面这种单例组件 你就完全没办法用 global 用 Context 本来就是要避免平凡的更新里面的东西 如果只需要取值 不需要刷新 可以用 Context 传递 Ref 我写全局单例组件的时候 一般也用 Global 的多 但是可复用组件 就不能用 Global 了 |
6
yyfearth 2022-05-07 15:53:00 +08:00
@XCFOX React 的 virtual dom 就是那个时代的产物
那个时代没有现在这么强大的工具链可以做到 Svelte 那样 要兼容浏览器也没有 Proxy 来做到原生 reactive 有了强大的编译器和现代浏览器 确实完全不需要 virtual dom 了 |
7
rioshikelong121 2022-05-07 15:55:22 +08:00
redux 也支持 hook API (useDispatch, useSelector) 啊,也不需要显式使用 connect ,我觉得这就够了。
比你个方案好的是支持 middleware 这样的机制。 |
8
FaiChou OP @rioshikelong121 的确, 代替的是 react-redux 而没有代替 redux, 因为 redux 支持中间件.
|
9
gouflv 2022-05-07 17:03:17 +08:00 via iPhone
context 是可以分层级管理的,作用域更小,同时还具有生命周期
|
11
chenliangngng 2022-05-08 12:30:54 +08:00
登录状态我这里就是全局变量+闭包控制的,没有用 redux 也没有用 hooks 也没有用 context 。原因很简单,我希望这个状态和 react 生态解耦。一些其他需要用到登录状态的中间件,我没必要用 react 的方式去写
看你业务场景吧,适合就是好的。微前端场景,完全可以全局变量+闭包;全局状态的场景,可以用 redux 也可以用 context ,而且尽量减少全局状态,尽量减少状态,少用状态管理,因为这会让项目指数级复杂化;一个模块的状态,用 context 是最好的,在模块最顶级文件使用,可以保证其他模块不去用,不需要 props 传参 |
12
shiye515 2022-05-08 22:42:42 +08:00
自己一个人开发的时候随便,很多人一起合作的时候根本找不到全局变量是被谁在什么时候改了
|