Next.js router.query行为不一致问题及解决方法咨询
解决Next.js动态路由slug旧值导致请求错误的问题
问题根源
你的代码里fetchPhotos直接引用外部的slug变量,路由切换时闭包会捕获旧的slug值,导致请求用错参数。另外useEffect的依赖只加了slug,没包含router.isReady,可能在路由未完全就绪时就触发请求,进一步加剧参数不一致的问题。
修复方案
方案1:将slug作为参数传入fetchPhotos,避免闭包陷阱
修改fetchPhotos让它接收slug作为参数,每次调用时传入最新值,确保请求用的是当前路由的参数:
const router = useRouter(); const { slug } = router.query; useEffect(() => { if (router.isReady && slug) { fetchPhotos(slug); } }, [router.isReady, slug]) // 添加router.isReady到依赖项 const fetchPhotos = (currentSlug) => { unsplash.search.getPhotos({ query: currentSlug, // 使用传入的最新slug page: 1, perPage, }) .then((result) => { if (result.status === 200 && result.type === "success") { dispatch({ type: "SET_PHOTOS", payload: result.response.result }) } else { console.log(result.errors); } }) .catch((info) => { console.log(info); }); };
方案2:监听路由完成事件,确保获取最新参数
利用Next.js Router的routeChangeComplete事件,在路由完全切换后再获取最新slug并触发请求:
const router = useRouter(); useEffect(() => { const handleRouteChange = () => { const { slug } = router.query; if (slug) { fetchPhotos(slug); } }; // 监听路由切换完成事件 router.events.on('routeChangeComplete', handleRouteChange); // 组件卸载时移除监听 return () => { router.events.off('routeChangeComplete', handleRouteChange); }; }, [router]) const fetchPhotos = (currentSlug) => { unsplash.search.getPhotos({ query: currentSlug, page: 1, perPage, }) .then((result) => { if (result.status === 200 && result.type === "success") { dispatch({ type: "SET_PHOTOS", payload: result.response.result }) } else { console.log(result.errors); } }) .catch((info) => { console.log(info); }); };
方案3:直接从router.query获取最新值
在fetchPhotos里直接读取router.query.slug,同时确保useEffect依赖包含router.isReady和router.query.slug:
const router = useRouter(); useEffect(() => { if (router.isReady && router.query.slug) { fetchPhotos(); } }, [router.isReady, router.query.slug]) const fetchPhotos = () => { const currentSlug = router.query.slug; // 直接从router.query取最新值 unsplash.search.getPhotos({ query: currentSlug, page: 1, perPage, }) .then((result) => { if (result.status === 200 && result.type === "success") { dispatch({ type: "SET_PHOTOS", payload: result.response.result }) } else { console.log(result.errors); } }) .catch((info) => { console.log(info); }); };
关键说明
- 闭包陷阱是核心问题:组件渲染时
fetchPhotos会捕获当时的slug值,路由切换后如果旧的请求还在执行,就会继续用旧值。把slug作为参数传入能彻底避免这个问题。 - 必须检查
router.isReady:确保路由参数完全加载完成后再触发请求,避免slug为undefined或旧值的情况。
内容的提问来源于stack exchange,提问作者Aman Ghanghoriya
相关产品推荐
相关产品推荐

