一个真的仅需一行代码就可以配置的 React 全局状态库,但是功能也仅限于此

2023-09-01 23:19:49 +08:00
 MakinoharaShoko

https://github.com/MakinoharaShoko/react-usevalue-hook

以下两个组件可以同步状态:

import { useValueWithKey } from 'react-usevalue-hook';

function Comp1() {
  const value1 = useValueWithKey(1, 'global1')

  return <div onClick={() => { value1.value = value1.value + 1 }}>
    {value1.value} Click to +1
  </div>
}

function Comp2() {
  const value2 = useValueWithKey(2, 'global1')

  return <div onClick={() => { value2.value = value2.value + 1 }}>
    {value2.value} Click to +1
  </div>
}

function App() {
  return (
    <div style={{padding:20}}>
      <div><Comp1 /></div>
      <div><Comp2 /></div>
    </div>
  )
}

export default App
1792 次点击
所在节点    React
15 条回复
SHF
2023-09-01 23:25:40 +08:00
https://github.com/ShenHongFei/react-object-model

我这个状态管理库也很简单,在组件里通过

const { name, age } = user.use(['name', 'age'])

订阅对象的属性,在属性改变时 diff, 重新渲染
MakinoharaShoko
2023-09-01 23:40:41 +08:00
@SHF 将一个对象转换为一个全局状态,挺有趣的
SHF
2023-09-01 23:45:22 +08:00
```ts
export function useValueWithKey<T> (initialState: T, key: string) {
const [_, setValue] = useState<T>(initialState)

useEffect(() => {
// init value(if not set by another component)
mkv.init(key, initialState)
const handleChange = () => {
setValue(mkv.get(key))
}
eb.on(`__CHANGED__${key}`, handleChange)
handleChange()
return () => {
eb.off(`__CHANGED__${key}`, handleChange)
}
}, [ ])

return {
set value (newValue: T) {
mkv.set(key, newValue)
eb.emit(`__CHANGED__${key}`)
setValue(newValue)
},
get value (): T {
return mkv.get(key) ?? initialState
}
}
}
```

useEffect 里面直接调用了 handleChange, 里面执行 setValue 会导致组件挂载之后因为 state 变了又重新 render ,不太好
你试试在组件里面 console.log('render') 看看渲染了几次
yhvictor
2023-09-01 23:46:40 +08:00
跟 jotai 差不多
MakinoharaShoko
2023-09-01 23:49:33 +08:00
@SHF #3 确实不太好,写这个是为了让两个不同初始值的状态能统一,如果不考虑这个问题就不用写这行代码了(我在想什么,给一个 key 的状态设置两个不同的初始值不是用户的问题吗)
MakinoharaShoko
2023-09-01 23:51:31 +08:00
@SHF #3 我来加个条件判断,如果获取到的结果等于初始值,就不设置
SHF
2023-09-02 00:05:00 +08:00
还有个问题,就是 MemoryKV 里面 map 里存了 key 对应的 value. 在 init 的时候添加进 map ,但是在所有使用这个 key 的组件都 unmount 时,应该删除 map 中的 key, 否则就会内存泄漏。map 除了要维护 value ,还要维护使用这个 key 的组件有多少,当使用的组件为 0 时做清理。但这个方法其实在 react 18 里面,如果启用了 strict 模式,组件会被模拟挂载两次,也不好搞,参考 https://stackoverflow.com/questions/72238175/why-useeffect-running-twice-and-how-to-handle-it-well-in-react
MakinoharaShoko
2023-09-02 00:07:58 +08:00
@SHF #7 确实有点不好搞,不过如果做成每次执行 useEffect 中的卸载函数时候 -1 ,每次 init 时 +1 ,应该可以解决
MakinoharaShoko
2023-09-02 00:10:47 +08:00
@SHF #7 不过这个库更多是用于在做小项目的时候偷懒,内存泄露只取决于 key 的数目,感觉也不会很庞大。我打算再优化优化
MakinoharaShoko
2023-09-02 01:35:03 +08:00
@SHF 突然想到一个问题,对于一个全局状态存储库,在组件卸载后把状态留下来,好像是一个正确的行为。其他全局状态存储库的状态初始化是和组件无关的,无论组件是否存在,状态都一直在内存中保留。而我这里只是把初始化放到了组件挂载过程中,其他行为应该要和其他全局状态存储库保持一致。
SHF
2023-09-02 12:35:20 +08:00
codehz
2023-09-02 13:06:34 +08:00
useEffect 里搞订阅容易在 react18 的 suspense 和异步模式中出问题
MakinoharaShoko
2023-09-02 22:33:12 +08:00
@codehz 是的,但是官方教程(老)确实是这样做的,之后想到新办法再来解决一下
codehz
2023-09-03 00:58:34 +08:00
@MakinoharaShoko react18 有专门的 useSyncExternalStore
MakinoharaShoko
2023-09-03 19:53:00 +08:00
@codehz #14 OK ,我去了解一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/970249

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX