Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] useGlobalState 全局状态 #1832

Closed
mushan0x0 opened this issue Aug 24, 2022 · 37 comments
Closed

[RFC] useGlobalState 全局状态 #1832

mushan0x0 opened this issue Aug 24, 2022 · 37 comments

Comments

@mushan0x0
Copy link
Contributor

mushan0x0 commented Aug 24, 2022

useGlobalState

React Hook 实现一个全局状态的功能,不同页面不同组件使用同一个状态。Preview →

QQ20220824-192401-HD.mp4

Usage

1.先为全局状态设置一个唯一key,或者还可以设置一个初始值

const initStep = 1;
const key = 'global-step';
const [step, setStep] = useGlobalState(key, initStep);
或者二次封装一个useGlobalStep
const useGlobalStep = (initStep: number = 0) => {
  const key = 'global-step';
  useGlobalState(key, initStep);
}

2.在其他组件或者页面引入useGlobalState并使用相同的key

const PageA = () => {
  const key = 'global-step';
  const [step, setStep] = useGlobalState(key, 1);
  return <div>
    {step}
    <Button onClick={() => setStep(step + 1)}>step + 1</Button>
  </div>
}

const PageB = () => {
  const key = 'global-step';
  const [step, setStep] = useGlobalState(key, 1);
  return <div>
    {step}
    <Button onClick={() => setStep(step - 1)}>step - 1</Button>
  </div>
}
或者使用二次封装的useGlobalStep
const PageA = () => {
  const [step, setStep] = useGlobalStep();
  return <div>
    {step}
    <Button onClick={() => setStep(step + 1)}>step + 1</Button>
  </div>
}

const PageB = () => {
  const [step, setStep] = useGlobalStep();
  return <div>
    {step}
    <Button onClick={() => setStep(step - 1)}>step - 1</Button>
  </div>
}

API

useGlobalState<T>(key: string, initState?: T): [T, (state: T) => void]

参数 类型 必须? 描述
key string Yes 全局状态的唯一key
initState any No 状态的初始值

Demo

Edit dumi-demo (forked)

@mushan0x0
Copy link
Contributor Author

@brickspert 有什么意见不

@mushan0x0
Copy link
Contributor Author

@awmleer

@brickspert
Copy link
Collaborator

关于全局状态管理,我们建议使用 hox,ahooks 里不建议加这个 Hooks

@brickspert
Copy link
Collaborator

感谢 RFC 哈!

@mushan0x0
Copy link
Contributor Author

mushan0x0 commented Aug 25, 2022

@brickspert 这个不一样,你看下里面的源码呢 https://codesandbox.io/s/dumi-demo-forked-73u0pv?file=/useGlobalState.tsx

@mushan0x0
Copy link
Contributor Author

这个是用批量更新实现的全局状态,不是用的context

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

实现是用全局的一个 globalStates 去管理,相当于脱离了 React 的范围之外了,这样好么?

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

那我用 Context 去管理岂不是更好么,感觉不到优势在哪呢

@mushan0x0
Copy link
Contributor Author

mushan0x0 commented Aug 25, 2022

可以实现组件卸载可以清除globalStates里面存的内容,优势在于不需要provider,useContext,createContext @hchlq

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

我的意思不是 ”组件卸载可以清除里面存的内容“。如果是 React 18 之后的,该如何考虑,状态管理是不建议脱离 React 的范围的

而且 createContext 我也可以封装,使用也很方便呀

@mushan0x0
Copy link
Contributor Author

我以为你考虑的是内存泄漏的问题,你可以举例说明可能会有什么问题吗 @hchlq

@mushan0x0
Copy link
Contributor Author

createContext 容易被滥用,开发者可能会往里面放很多东西,useGlobalState设计跟useState是一样的,可以防止这一点

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

没有特别好的举例,但是状态尽量不要脱离 React 的范围~

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

滥用是相对而言的,团队中通过一些开发原则就可以避免了

@mushan0x0
Copy link
Contributor Author

但是这样也没有脱离react范围,实际设置状态和读取状态都是react的useState

@hchlq
Copy link
Collaborator

hchlq commented Aug 25, 2022

但是你会间接额外的维护状态和更新函数呀,我觉得不是特别有必要,看下 @brickspert 怎么说~

@miracles1919
Copy link
Collaborator

你的 demo 看起来比较简单,怎么处理多个数据呢

比如有 Global-A、 Global-B、Global-C,看起来得这样..?

const useGlobalA = () => useGlobalState('A');
const useGlobalB = () => useGlobalState('B');
const useGlobalC = () => useGlobalState('C');

const A = () => {
  const [globalA] = useGlobalA();
  const [globalB] = useGlobalB();
  const [globalC] = useGlobalC();
  return <div>组件A:{globalA}{globalB}{globalC}</div>;
};

@mushan0x0
Copy link
Contributor Author

@miracles1919 是的,它跟useState一样,只不过多了个key

@miracles1919
Copy link
Collaborator

那感觉实用性不是很大啊... 真实的场景下往往是需要聚合多组数据的,个人觉得没有必要加在 ahooks 里

关于全局状态管理,我们推荐使用 hox

@mushan0x0
Copy link
Contributor Author

mushan0x0 commented Aug 26, 2022

多个数据组合也可以这样用,useState可以怎么用,它就可以怎么用

const useGlobalABC = () => {
  const [globalA] = useGlobalState('A');
  const [globalB] = useGlobalState('B');
  const [globalC] = useGlobalState('C');
  return { globalA, globalB, globalC };
};
const A = () => {
  const {globalA, globalB, globalC} = useGlobalABC();
  return <div>组件A:{globalA}{globalB}{globalC}</div>;
};

@mushan0x0
Copy link
Contributor Author

@miracles1919 组合使用的例子我加了,你看看

@awmleer
Copy link
Collaborator

awmleer commented Aug 27, 2022

我有个小问题,如果在多个组件中都调用了 useGlobalStatekey 是同一个,但是 initialValue 值不一样,这种情况感觉还挺有问题的?

const Foo = () => {
  const [counter, setCounter] = useGlobalState('counter', 1)
  // ...
}

const Bar = () => {
  const [counter, setCounter] = useGlobalState('counter', 2)
  // ...
}

@mushan0x0
Copy link
Contributor Author

我有个小问题,如果在多个组件中都调用了 useGlobalStatekey 是同一个,但是 initialValue 值不一样,这种情况感觉还挺有问题的?

const Foo = () => {

  const [counter, setCounter] = useGlobalState('counter', 1)

  // ...

}



const Bar = () => {

  const [counter, setCounter] = useGlobalState('counter', 2)

  // ...

}

key重复是不好避免的问题,但是像useLocalStorage这些都是用的key,大家普遍在用一般不会遇到什么问题。初始值只会在第一个使用到的地方才会生效,这个容易让用户困惑,但是可以加一些警告说初始值没有生效来避免

@miracles1919
Copy link
Collaborator

这么说的话... useLocalStorage 完全能代替啊

@mushan0x0
Copy link
Contributor Author

这么说的话... useLocalStorage 完全能代替啊

useLocalStorage不会更新所有用到的地方啊....你看看例子呢。而且有全局状态需求的地方并不一定有存储local storage的需求啊

@brickspert
Copy link
Collaborator

我的结论是,这个 Hook 只适合简单的项目,稍微复杂的项目还是要引入正儿八经的状态库的。

@mushan0x0
Copy link
Contributor Author

我的结论是,这个 Hook 只适合简单的项目,稍微复杂的项目还是要引入正儿八经的状态库的。

很早期我就有在大大小小的项目使用了,目前没什么问题,甚至有很多好处。这个就是使用很简单,如果项目是需要靠使用门槛来限制全局状态的使用,用别的状态库也是可以的。

@miracles1919
Copy link
Collaborator

用 key 这个方案,设计上就是有缺陷的,例如 #1832 (comment) 写的 demo,没有办法解决重复 key 的问题

设计上的问题,需要人为的方式避免(比如警告?),肯定是不合理的,一旦项目规模或者团队规模起来了,就是在给他们埋坑

这个就在你的项目中自己维护吧

@mushan0x0
Copy link
Contributor Author

useLocalStorage难道大型项目里面不能用吗,用了useLocalStorage就是在埋坑吗

@miracles1919
Copy link
Collaborator

如果 useLocalStorage 用于全局状态,那确实是在埋坑...

@mushan0x0
Copy link
Contributor Author

你的全局状态概念还是传统的里面乱七八糟放很多东西,这种情况全局状态不好管理,但这个全局状态就只是一个状态了,副作用没那么大。

比如一个暗色主题切换,主题色切换,第一直觉就会让人用这个工具,他们本身就是一个状态而已。

@mushan0x0
Copy link
Contributor Author

而且这样状态颗粒度低,性能也好优化,避免了传统全局状态里面放的东西太多,带来的不必要的渲染

@miracles1919
Copy link
Collaborator

1、我上面说的是这个 Hook 作为全局状态,设计上的弊端

  • 用 key 管理状态,多人协作容易出现覆盖的问题
  • 调用 useGlobalState 需要传入 initialValue,会导致状态的不可预测

结合起来就是 #1832 (comment) 这个例子,使用 Foo 组件的时候 Count 是 1,使用 Bar 组件的时候 Count 变成了 2,反而会增加复杂性

2、对于全局状态的数据规模,如果复杂度比较低,比如一个主题色切换,那使用传统的状态管理一样有这些优点
"状态颗粒度低、性能也好优化、避免带来的不必要的渲染"

但是反过来,如果全局状态有一定的复杂度,用 useGlobalState 反而不好管理了

所以,这个 RFC 个人觉得没有必要,先关了。有问题可以继续讨论哈

@mushan0x0
Copy link
Contributor Author

一般使用都是会二次封装比如例子useGlobalStep,因为没人想重复去写key和初始值还有类型,复杂例子我也写了的useGlobalABC,我个人觉得很多人被redux、dva给教化了,没有需求制造需求,简单的反而是异类了

@brickspert
Copy link
Collaborator

一般使用都是会二次封装比如例子useGlobalStep,因为没人想重复去写key和初始值还有类型,复杂例子我也写了的useGlobalABC,我个人觉得很多人被redux、dva给教化了,没有需求制造需求,简单的反而是异类了

我现在一直用 hox,很简单的一个状态管理库。

@SignDawn
Copy link

我认为这个 hook 还是挺有意思的,有点类似于 vue 中将 ref 变量放到 composition api 外。
所谓的 key 和 初始值问题,可以稍微改造一下。
让 开发者 不传这个,传一个对象,如果是同一个对象,那么就是同一个全局状态;这样初始值就没有歧义了。state 和 sets 都存储到这个对象里。
image

image

改造了一下 demo ,可以参考。https://codesandbox.io/s/dumi-demo-forked-z6j9zh?file=/combination-use.tsx

@mushan0x0
Copy link
Contributor Author

mushan0x0 commented Sep 27, 2023

reactjs/rfcs#224 (comment) react-use 已经有了。要解决 key 重复,用 Symbo 代替字符串就行了。最大的问题是初始化值,其实加个警告说已经初始化过了也可以。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants