NextJS13本地Fetch返回500:如何结合Context实现数据获取?
NextJS13 App Router:本地路由数据与React Context结合的异常问题
问题详情
切换至NextJS13并使用新的App Router时,遇到Fetch返回异常问题。目录结构如下:
/app /context index.tsx /measurement route.ts
Context Provider 代码(/app/context/index.tsx)
export const MeasurementContext = createContext([]); const fetcher = (...args) => fetch(...args); const MeasurementProvider = ({ children }) => { const [elements, setElements] = useState([]); const [useSwr, setUseSwr] = useState(false); const { data, isLoading, error } = useSWR(useSwr ? "/measurement" : null, fetcher); useEffect(() => { if (!elements.length) { setUseSwr(true); } }, [elements]); useEffect(() => { setUseSwr(false); data && setElements(data); }, [data]); return <MeasurementContext.Provider value={elements}>{children}</MeasurementContext.Provider>; }; export default MeasurementProvider;
路由处理代码(/app/measurement/route.ts)
export async function GET() { try { const data = await getMeasurements(); if (data.length) { return data; } } catch (e) { console.error(e, "error msg"); } }
已知路由可返回有效数据,但Context Provider中出现500内部服务器错误。已尝试方案:
- 使用NextResponse返回JSON格式数据
- 尝试通用Fetch并在多组件中测试响应差异
需求:如何在NextJS13中结合本地路由数据与React Context?
解决方案
1. 修复API路由的响应格式
NextJS13 App Router要求API路由必须返回NextResponse对象,直接返回原始数据会触发500错误。修改/app/measurement/route.ts:
import { NextResponse } from 'next/server'; export async function GET() { try { const data = await getMeasurements(); // 统一返回JSON格式响应,无论数据是否为空 return NextResponse.json(data); } catch (e) { console.error(e, "error msg"); // 捕获错误并返回标准错误响应 return NextResponse.json({ error: '获取测量数据失败' }, { status: 500 }); } }
2. 简化Context Provider逻辑
原代码中useSwr的状态切换逻辑冗余,容易引发状态循环。同时,App Router下的客户端组件必须显式声明'use client';才能使用React Hooks。修改后的/app/context/index.tsx:
'use client'; import { createContext, useState, useEffect } from 'react'; import useSWR from 'swr'; export const MeasurementContext = createContext({ elements: [], isLoading: false, error: null }); // 完善fetcher函数,确保解析响应并处理错误 const fetcher = async (url: string) => { const res = await fetch(url); if (!res.ok) throw new Error(`请求失败:${res.status}`); return res.json(); }; const MeasurementProvider = ({ children }) => { const [elements, setElements] = useState([]); const { data, error, isLoading } = useSWR('/measurement', fetcher); // 当SWR获取到数据时更新Context状态 useEffect(() => { if (data) setElements(data); }, [data]); return ( <MeasurementContext.Provider value={{ elements, isLoading, error }}> {children} </MeasurementContext.Provider> ); }; export default MeasurementProvider;
3. 全局挂载Context Provider
在app/layout.tsx中挂载Provider,确保所有子组件都能访问Context:
import MeasurementProvider from './context'; export default function RootLayout({ children }) { return ( <html lang="zh-CN"> <body> <MeasurementProvider>{children}</MeasurementProvider> </body> </html> ); }
4. 关键注意事项
- 客户端组件声明:App Router中使用
useState、useSWR等Hooks的组件必须添加'use client';指令,否则会在服务端渲染时出错。 - Fetcher函数解析:原代码中直接返回
fetch(...args),未解析响应为JSON,导致SWR的data是Response对象而非实际数据,需通过res.json()解析。 - 错误处理:API路由和Fetcher函数都要添加错误捕获,避免未处理的异常导致500错误。
内容的提问来源于stack exchange,提问作者HJW
相关产品推荐
相关产品推荐

