React路由切换时useEffect触发但未更新状态,自定义Hook数据正常问题排查
问题原因
- useEffect依赖项不符合规则:你当前的useEffect仅将
customers加入依赖数组,但内部逻辑实际用到了user和keycloak两个变量。路由切换后组件重新挂载时,customers可能先于user、keycloak加载完成,此时触发useEffect时user?.email为undefined,过滤得到的filteredCustomers是空数组;后续user、keycloak初始化完成后,因为没有被加入依赖数组,useEffect不会重新执行,状态就会一直保留为空。刷新页面时user、keycloak往往已经提前完成初始化,所以过滤逻辑可以正常执行。 - 异步请求存在竞态问题:useEffect内部的
Promise.all没有添加取消逻辑,路由切换组件卸载后,未完成的请求在返回后依然会调用setLoans,可能引发内存泄漏或状态错乱。
修复方案
补全useEffect的依赖项,同时添加组件挂载状态判断避免卸载后更新状态,参考代码如下:
export const Overview: React.FC<OverviewProps> = ({ user }) => { const { keycloak } = useKeycloak() const { state } = useContext(DashboardContext) const [customers] = useLoanCustomers(state.contents.date) const [userCustomers, setUserCustomers] = useState<DwCustomerHeadline[]>([]) const [loans, setLoans] = useState<DwLoanContract[]>([]) useEffect(() => { // 标记当前组件挂载状态 let isMounted = true // 依赖未就绪时不执行逻辑 if (!user?.email || !keycloak) return const filteredCustomers = customers.filter((customer) => customer.stafferEmail === user.email) setUserCustomers(filteredCustomers) Promise.all( filteredCustomers.map((customer) => getCustomerLoans(keycloak, customer.counterpartyId)) ).then((res) => { // 仅组件未卸载时更新状态 if (isMounted) { setLoans(res.flat()) } }) // 组件卸载时标记为已卸载 return () => { isMounted = false } // 补全所有内部用到的依赖 }, [customers, user, keycloak]) // 其余渲染逻辑保持不变 return ( <> <div className="grid gap-4 p-2 md:grid-cols-3 sm:grid-cols-1"> <div className="col-span-2 shadow-sm"> <KeyMetrics /> </div> <div className="shadow-sm"> <NextReview customers={userCustomers} /> </div> </div> <div className="grid gap-4 p-2 md:grid-cols-3 sm:grid-cols-1 grid-rows-2"> <div className="md:h-80 col-span-2 shadow-sm"> <ErpOverview customers={userCustomers} /> </div> <div className="row-span-2 shadow-sm" style={{ height: '34rem' }}> <Outflow loans={loans} endDate={state.contents.date} /> </div> <div className="h-52 col-span-2 shadow-sm"> <LoanArrears customers={userCustomers} /> </div> </div> </> ) }
内容的提问来源于stack exchange,提问作者SchLu
相关产品推荐
相关产品推荐

