如何优化含异步API调用的React页面?useEffect依赖router.query场景
React + Next.js 客户端异步请求优化方案
以下方案均为客户端侧发起请求,API返回的set-cookie会正常写入客户端浏览器,符合你无法使用getServerSideProps的场景限制。
1 基础修复版:解决原实现的隐性问题
注意:useEffect 不允许直接传入 async 函数,会导致内存泄漏警告,因为 React 期望 useEffect 的返回值是清理函数而非 Promise。
同时新增router.isReady判断,确保路由 hydration 完成后再读取query,避免 hydration 过程中query为空导致的逻辑异常,修复后代码如下:
const MyPage = () => { const [user, setUser] = useState(null) const router = useRouter() useEffect(() => { // 路由未初始化完成/query无有效值直接跳过 if (!router.isReady || !router.query.something) return const fetchUser = async () => { try { const res = await api.get(`/resetTokens/${router.query.something}`) setUser(res.data) } catch (err) { setUser(null) } } fetchUser() }, [router.isReady, router.query.something]) return (<div>...</div>) }
2 可复用封装:自定义请求Hook
如果项目中有多个同类场景,可以把逻辑抽成通用自定义Hook,页面代码会大幅简化:
// 全局通用Hook,可复用到所有依赖路由query的客户端请求场景 const useQueryByRouteParam = (paramKey, fetcher) => { const [data, setData] = useState(null) const [error, setError] = useState(null) const router = useRouter() useEffect(() => { if (!router.isReady || !router.query[paramKey]) return const fetchData = async () => { try { const result = await fetcher(router.query[paramKey]) setData(result) } catch (err) { setError(err) setData(null) } } fetchData() }, [router.isReady, router.query[paramKey], fetcher]) return { data, error } } // 页面内使用 const MyPage = () => { const { data: user } = useQueryByRouteParam('something', async (something) => { const res = await api.get(`/resetTokens/${something}`) return res.data }) return (<div>...</div>) }
3 生产级最优解:使用专业数据请求库
如果项目允许引入SWR 或 TanStack Query(原React Query)这类数据请求库,无需手动维护状态、处理竞态、防重复请求,写法最简洁且稳定性最高:
以SWR为例:
import useSWR from 'swr' const MyPage = () => { const router = useRouter() // SWR会自动跳过null值的请求,无需手动判断拦截 const { data: user, error, isLoading } = useSWR( router.isReady && router.query.something ? `/resetTokens/${router.query.something}` : null, (url) => api.get(url).then(res => res.data) ) // 自带加载、错误状态,不用额外写state维护 if (isLoading) return <div>加载中</div> if (error) return <div>请求失败</div> return (<div>...</div>) }
内容的提问来源于stack exchange,提问作者Glenn
相关产品推荐
相关产品推荐

