Gatsby中使用useLocation钩子获取URL参数时服务端渲染(SSR)报错问题求助
为什么会出现这个问题?
Gatsby 采用**服务端渲染(SSR)**和静态站点生成(SSG)的混合模式,在构建或服务端渲染阶段,代码是运行在 Node.js 环境中的——这时候没有浏览器的 window 对象,也无法获取当前浏览器的 URL 上下文。
你用 useLocation().search 拿到的结果,在浏览器端是包含 ?page=7 的字符串,但在 Node.js 环境里是空字符串或 undefined。当你执行 split('=')[1] 时,空字符串拆分后只会得到一个元素的数组,取索引 [1] 就会返回 undefined,转成 Number 自然就是 NaN。
而 Gatsby 要求 React 组件在浏览器和 Node.js 环境中必须渲染出一致的内容,这种两端不一致的结果(浏览器是数字,SSR 是 NaN)就触发了报错。
解决方案
这里有几种可靠的解决方式,你可以根据自己的场景选择:
1. 用 useEffect 延迟获取参数(最通用)
useEffect 钩子只会在浏览器端执行,SSR 阶段不会运行,这样就能避免 Node 环境下的异常:
import { useLocation } from '@reach/router'; import { useState, useEffect } from 'react'; const JobOffersPage = () => { const [pageParam, setPageParam] = useState(1); // 设置默认页码 const location = useLocation(); useEffect(() => { // 用 URLSearchParams 解析查询参数,比手动 split 更健壮 const searchParams = new URLSearchParams(location.search); const pageValue = searchParams.get('page'); // 校验参数有效性,避免非数字值 setPageParam(pageValue && !isNaN(Number(pageValue)) ? Number(pageValue) : 1); }, [location.search]); return <div>当前页码:{pageParam}</div>; }; export default JobOffersPage;
2. 直接判断浏览器环境
通过 typeof window !== 'undefined' 区分环境,在 SSR 时使用默认值,浏览器端再解析参数:
import { useLocation } from '@reach/router'; const JobOffersPage = () => { const location = useLocation(); // 默认页码设为1,保证SSR时的渲染一致性 let pageParam = 1; if (typeof window !== 'undefined') { const searchParams = new URLSearchParams(location.search); const pageValue = searchParams.get('page'); pageParam = pageValue && !isNaN(Number(pageValue)) ? Number(pageValue) : 1; } return <div>当前页码:{pageParam}</div>; }; export default JobOffersPage;
3. 用 getServerData 获取服务端请求参数(Gatsby v4+)
如果你的页面是服务端渲染模式(比如使用 Gatsby Cloud 或自托管 SSR),可以在页面组件中导出 getServerData,直接从服务端请求中获取查询参数:
// 仅适用于 Gatsby v4 及以上的页面组件 export async function getServerData({ request }) { const url = new URL(request.url); const pageValue = url.searchParams.get('page'); const pageParam = pageValue && !isNaN(Number(pageValue)) ? Number(pageValue) : 1; return { props: { pageParam }, }; } const JobOffersPage = ({ pageParam }) => { return <div>当前页码:{pageParam}</div>; }; export default JobOffersPage;
额外提醒
尽量避免手动用 split 解析 URL 查询参数,因为如果参数顺序变化、存在多个参数或者参数值包含特殊字符,这种方式很容易出错。URLSearchParams 是浏览器原生的 API,能更安全可靠地处理查询参数。
内容的提问来源于stack exchange,提问作者tateccf

