Next.js项目中使用useSWR请求Firebase实时数据库时data与error均返回null的问题
我在Next.js项目中尝试使用useSWR请求Firebase实时数据库,但不知为何data和error变量始终返回null。相关代码如下:
import useSWR from 'swr'; const LastSales: NextPage = () => { const [sales, setSales] = useState<Sale[]>([]); const { data, error } = useSWR( 'https://my-app-12345-default-rtdb.firebaseio.com/sales.json' ); useEffect(() => { if (!data) return; const salesArr = []; for (const key in data) { salesArr.push({ id: key, username: data[key].username, volume: data[key].volume, }); } setSales(salesArr); }, [data]); if (error) return <p>Failed to load.</p>; if (sales.length === 0) return <p>Loading...</p>; return <div>Sales List</div>
使用fetch发起相同请求时完全正常,但代码量是useSWR方式的10倍。请问为何data和error都会等于null?
可能的原因及解决方案
1. 未显式指定fetcher函数(最常见原因)
虽然useSWR自带默认的fetcher,但它的逻辑比较简单(仅调用fetch(url).then(res => res.json())),没有处理HTTP错误状态码(比如4xx/5xx),也可能在某些环境下无法正确解析Firebase的响应。当请求出现异常时,默认fetcher可能不会正确抛出错误,导致data和error都处于null状态。
解决方法:手动定义一个带错误处理的fetcher,并传递给useSWR:
// 定义自定义fetcher const fetcher = async (url: string) => { const res = await fetch(url); // 检查响应状态 if (!res.ok) { throw new Error(`请求失败: ${res.status}`); } return res.json(); }; // 在useSWR中使用该fetcher const { data, error } = useSWR( 'https://my-app-12345-default-rtdb.firebaseio.com/sales.json', fetcher );
2. Next.js渲染模式导致客户端请求未触发
useSWR是客户端优先的数据获取库,默认情况下在服务器端渲染(SSR)或静态生成(SSG)阶段不会发起请求。如果你的页面是SSR/SSG模式,初始渲染时data会保持null,直到客户端hydration完成后才会发起请求。
解决方法:
- 调整加载状态的判断逻辑,直接基于
data和error判断,而不是依赖衍生的sales状态:if (error) return <p>Failed to load.</p>; if (!data) return <p>Loading...</p>; // 直接检查data是否存在 - 如果需要在服务器端预获取数据,可以结合Next.js的
getServerSideProps或getStaticProps,再将数据传递给组件,同时用useSWR做客户端的重新验证。
3. Firebase安全规则或认证问题
虽然你提到fetch请求正常,但需要确认useSWR的请求是否携带了必要的认证信息(如果你的Firebase数据库要求认证)。默认情况下,useSWR的fetcher不会自动携带认证凭证,而你手动写的fetch可能已经处理了认证。
解决方法:如果数据库需要认证,在fetcher中添加认证头:
import { getAuth } from 'firebase/auth'; const fetcher = async (url: string) => { const auth = getAuth(); const idToken = await auth.currentUser?.getIdToken(); const res = await fetch(url, { headers: idToken ? { Authorization: `Bearer ${idToken}` } : {} }); if (!res.ok) throw new Error('请求失败'); return res.json(); };
4. useSWR版本问题
旧版本的useSWR可能存在一些兼容性bug,导致请求无法正常触发。建议更新到最新版本:
npm install swr@latest # 或 yarn add swr@latest
简化代码的建议
你可以直接基于useSWR返回的data转换为sales数组,无需额外的useState和useEffect,让代码更简洁:
import useSWR from 'swr'; const LastSales: NextPage = () => { const fetcher = async (url: string) => { const res = await fetch(url); if (!res.ok) throw new Error('Failed to load sales'); return res.json(); }; const { data, error } = useSWR( 'https://my-app-12345-default-rtdb.firebaseio.com/sales.json', fetcher ); if (error) return <p>Failed to load.</p>; if (!data) return <p>Loading...</p>; // 直接转换data为sales数组 const sales = Object.entries(data).map(([key, value]) => ({ id: key, username: value.username, volume: value.volume })); return <div>Sales List: {sales.length} items</div>; }; export default LastSales;
内容的提问来源于stack exchange,提问作者chocojunkie

