React自定义Hook状态变更无法同步更新所有引用它的组件
问题本质
你当前编写的useData自定义Hook,仅实现了逻辑复用,没有实现跨组件的状态共享。
React自定义Hook的基础设计规则是:Hook内部定义的所有useState状态,都会和当前调用Hook的组件实例绑定,每个组件调用Hook时都会拿到一套完全独立的状态实例。
- 组件A调用
useData()时,会创建A专属的isLoading状态,以及专门修改这个状态的setIsLoading方法 - 组件B调用
useData()时,会创建另一套完全不连通的isLoading状态和对应的修改方法
因此A触发fetch时,只会修改自己组件内部的isLoading,自然完全影响不到组件B的状态。
需要补充的关键实现
要让A、B两个组件拿到同步的isLoading状态,需要把原本存在组件内部的共享状态,提升到组件之外的独立作用域托管,让所有调用Hook的组件都能读写同一份状态。
最轻量化的实现方式是把状态提到Hook所在模块的顶层作用域,搭配简单的订阅通知机制,让所有消费这个状态的组件都能感知到状态变更:
// 状态脱离组件实例,放在模块全局作用域 let sharedLoadingState = false; // 存储所有订阅状态的组件更新函数 const loadingListenerSet = new Set<(state: boolean) => void>(); export const useData = (): [boolean, () => Promise<void>] => { // 本地状态和全局共享状态初始化值对齐 const [localIsLoading, setLocalIsLoading] = useState(sharedLoadingState); // 组件挂载时注册状态更新监听,卸载时移除监听避免内存泄漏 useEffect(() => { loadingListenerSet.add(setLocalIsLoading); return () => { loadingListenerSet.delete(setLocalIsLoading); }; }, []); const fetch = async () => { // 修改全局共享状态后,通知所有订阅的组件同步更新本地状态 sharedLoadingState = true; loadingListenerSet.forEach(setter => setter(sharedLoadingState)); await new Promise<void>(resolve => setTimeout(resolve, 2500)); sharedLoadingState = false; loadingListenerSet.forEach(setter => setter(sharedLoadingState)); }; return [localIsLoading, fetch]; };
除此之外也可以依托React自带的机制实现状态共享:把isLoading状态和fetch方法放到React Context中,在应用根层级通过Provider托管这份数据,所有子组件通过Context接口消费共享数据,就不需要手动编写订阅逻辑,依托React自身的Context更新链路就能完成跨组件的状态同步。如果项目已经引入了全局状态管理工具,也可以直接把这份状态托管到全局store中,实现效果一致。
常见认知误区:很多开发者会误以为自定义Hook可以跨组件共享状态,实际上Hook仅负责抽离复用重复的逻辑,状态的跨组件共享必须靠外部的全局存储/Context机制实现。
内容的提问来源于stack exchange,提问作者Sloth Armstrong
相关产品推荐
相关产品推荐

