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
![]() | 1 SHF 2023-09-01 23:25:40 +08:00 https://github.com/ShenHongFei/react-object-model 我这个状态管理库也很简单,在组件里通过 const { name, age } = user.use(['name', 'age']) 订阅对象的属性,在属性改变时 diff, 重新渲染 |
![]() | 2 MakinoharaShoko OP @SHF 将一个对象转换为一个全局状态,挺有趣的 |
![]() | 3 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') 看看渲染了几次 |
4 yhvictor 2023-09-01 23:46:40 +08:00 跟 jotai 差不多 |
![]() | 5 MakinoaraShoko OP @SHF #3 确实不太好,写这个是为了让两个不同初始值的状态能统一,如果不考虑这个问题就不用写这行代码了(我在想什么,给一个 key 的状态设置两个不同的初始值不是用户的问题吗) |
![]() | 6 MakinoharaShoko OP @SHF #3 我来加个条件判断,如果获取到的结果等于初始值,就不设置 |
![]() | 7 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 |
![]() | 8 MakinoharaShoko OP @SHF #7 确实有点不好搞,不过如果做成每次执行 useEffect 中的卸载函数时候 -1 ,每次 init 时 +1 ,应该可以解决 |
![]() | 9 MakinoharaShoko OP @SHF #7 不过这个库更多是用于在做小项目的时候偷懒,内存泄露只取决于 key 的数目,感觉也不会很庞大。我打算再优化优化 |
![]() | 10 MakinoharaShoko OP @SHF 突然想到一个问题,对于一个全局状态存储库,在组件卸载后把状态留下来,好像是一个正确的行为。其他全局状态存储库的状态初始化是和组件无关的,无论组件是否存在,状态都一直在内存中保留。而我这里只是把初始化放到了组件挂载过程中,其他行为应该要和其他全局状态存储库保持一致。 |
![]() | 11 SHF 2023-09-02 12:35:20 +08:00 @MakinoharaShoko 喔对 |
![]() | 12 codehz 2023-09-02 13:06:34 +08:00 useEffect 里搞订阅容易在 react18 的 suspense 和异步模式中出问题 |
![]() | 13 MakinoharaShoko OP @codehz 是的,但是官方教程(老)确实是这样做的,之后想到新办法再来解决一下 |
![]() | 14 codehz 2023-09-03 00:58:34 +08:00 via iPhone @MakinoharaShoko react18 有专门的 useSyncExternalStore |
![]() | 15 MakinoharaShoko OP @codehz #14 OK ,我去了解一下 |