React useEffect搭配React-Query useQuery全局错误处理错误重复添加问题
问题根因
- AlertProvider每次渲染都会重新生成context的value对象,导致
useAlertContext()返回的引用持续变化,触发useAccess中useEffect的重复执行。 - React Query请求失败后,
query.isError和query.error会长期保持错误状态,不会因为全局alert被清空就自动重置,只要useEffect触发就会满足判断条件,重新添加错误。
修复方案
1. 稳定AlertContext的value引用(基础修复)
用useMemo包裹context的value,只有依赖项变化时才生成新对象,避免不必要的引用更新:
import React, { useState, createContext, useContext, useMemo } from 'react' const AlertContext = createContext() const AlertProvider = ({ children }) => { const [alert, setAlert] = useState(null) const removeAlert = () => setAlert(null) const addAlert = (message) => setAlert(message) // 稳定context引用 const contextValue = useMemo(() => ({ alert, add: addAlert, remove: removeAlert }), [alert]) return ( <AlertContext.Provider value={contextValue}> {children} </AlertContext.Provider> ) } const useAlertContext = () => { return useContext(AlertContext) } export { AlertProvider as default, useAlertContext }
2. 优化错误添加逻辑,避免重复添加
在useAccess的useEffect中增加重复判断,相同错误不会重复插入:
useEffect(() => { const errorMsg = query.error?.toString() if (query.isError && alertContext.alert !== errorMsg) { alertContext.add(errorMsg) } }, [alertContext, query.isError, query.error])
3. 最优方案:使用React Query原生onError回调
直接用useQuery内置的错误回调,请求失败时只会触发一次,完全规避useEffect依赖导致的重复执行问题,不需要额外写useEffect:
export const useAccess = () => { const alertContext = useAlertContext() const route = '/accessx' const query = useQuery(route, async () => await ApiV4.get(route), { retry: 0, // 错误回调仅在请求失败时触发一次 onError: (err) => alertContext.add(err.toString()) }) return query }
内容的提问来源于stack exchange,提问作者Adam Youngers
相关产品推荐
相关产品推荐

