Next.js 13+TypeScript异步组件获取数据时JSX组件类型错误求助
解决Next.js 13 + TypeScript异步组件Filter的JSX类型错误
错误的核心原因很明确:你把Filter组件写成了async函数,导致它的返回值是Promise<JSX.Element>,但React要求组件必须返回ReactElement(即JSX元素)或null,TypeScript因此抛出类型不匹配的错误。
下面分场景给出解决方案:
方案1:App Router下的Server Component(无交互需求)
如果你的Filter组件仅用于展示分类、不需要用户交互,可以直接利用Next.js 13 App Router对Server Component的原生异步支持,无需额外处理:
// app/components/Filter.tsx import api from '@/lib/api'; // Server Component 允许直接定义为async函数 async function Filter() { const categories = await api.get('/categories/'); return ( <div className="filter-container"> {categories.map(category => ( <div key={category.id} className="filter-item"> {category.name} </div> ))} </div> ); } export default Filter;
注意:Server Component不能使用React Hooks(如useState、useEffect),如果需要交互功能,使用下面的方案。
方案2:App Router/Pages Router下的Client Component(有交互需求)
如果组件需要交互(比如点击筛选、切换状态),必须用useEffect结合状态来处理异步请求,不能直接把组件定义为async函数:
// 适用于app目录(需加'use client')或pages目录的组件 'use client'; // App Router下必须添加这行,Pages Router可省略 import { useState, useEffect } from 'react'; import api from '@/lib/api'; // 定义分类类型,满足TypeScript类型校验需求 type Category = { id: number; name: string; }; function Filter() { const [categories, setCategories] = useState<Category[]>([]); const [isLoading, setIsLoading] = useState(true); const [errorMsg, setErrorMsg] = useState<string | null>(null); // 组件挂载后发起异步请求 useEffect(() => { const fetchCategories = async () => { try { const data = await api.get('/categories/'); setCategories(data); } catch (err) { setErrorMsg('分类加载失败,请稍后重试'); } finally { setIsLoading(false); } }; fetchCategories(); }, []); // 处理加载、错误状态 if (isLoading) return <div>加载中...</div>; if (errorMsg) return <div className="text-red-500">{errorMsg}</div>; return ( <div className="filter-wrapper"> <h4>筛选分类</h4> {categories.map(category => ( <label key={category.id} className="flex items-center gap-2"> <input type="checkbox" value={category.id} /> <span>{category.name}</span> </label> ))} </div> ); } export default Filter;
额外优化:用数据请求库简化逻辑
如果项目中有多个异步请求场景,推荐使用React Query或SWR这类库替代useEffect+state,代码更简洁,还自带缓存、重试、自动刷新等功能:
'use client'; import { useQuery } from '@tanstack/react-query'; import api from '@/lib/api'; type Category = { id: number; name: string }; function Filter() { const { data: categories, isLoading, error } = useQuery({ queryKey: ['categories'], queryFn: () => api.get('/categories/'), }); if (isLoading) return <div>加载中...</div>; if (error) return <div>加载失败</div>; return ( <div className="filter-wrapper"> <h4>筛选分类</h4> {categories?.map(category => ( <label key={category.id} className="flex items-center gap-2"> <input type="checkbox" value={category.id} /> <span>{category.name}</span> </label> ))} </div> ); } export default Filter;
内容的提问来源于stack exchange,提问作者Mateusz Michalik
相关产品推荐
相关产品推荐

