Next.js 13中添加"use client"后出现React子元素渲染错误求助
错误原因
Next.js 13的服务器组件支持直接用async/await获取数据并渲染,因为服务器端预渲染时会等待Promise resolve后再生成HTML。但当你给组件加上"use client"后,组件变成客户端组件——客户端组件的渲染逻辑在浏览器中执行,React无法直接渲染async组件函数返回的Promise对象,这就是你看到Error: Objects are not valid as a React child (found: [object Promise])的根本原因。
解决方法
方案1:分离数据获取与交互逻辑(最优解)
保留服务器组件负责数据获取,只把需要交互的部分抽成独立的客户端组件,通过props传递数据:
- 服务器组件(负责数据获取,无需
"use client")
// app/[classe]/page.jsx async function fetchClassData(classeName) { const res = await fetch(`https://www.dnd5eapi.co/api/classes/${classeName}`); return res.json(); } export default async function ClassPage({ params }) { const classData = await fetchClassData(params.classe); return ( <div> <h1>{classData.name}</h1> {/* 将数据传给客户端交互组件 */} <InteractiveSkillList skills={classData.skills} /> </div> ); }
- 客户端组件(负责交互,加
"use client")
// app/components/InteractiveSkillList.jsx "use client"; export default function InteractiveSkillList({ skills }) { const handleSkillClick = (desc) => { // 这里写你的点击交互逻辑,比如弹窗展示详情 alert(desc); }; return ( <ul> {skills.map((skill) => ( <li key={skill.index} onClick={() => handleSkillClick(skill.desc)} style={{ cursor: 'pointer', color: '#0070f3' }} > {skill.name} </li> ))} </ul> ); }
方案2:在客户端组件内获取数据
如果必须在客户端组件中获取数据,不能直接用async组件函数,要结合useState+useEffect或者数据获取库(如SWR、React Query)处理异步逻辑:
用useState+useEffect实现
"use client"; import { useState, useEffect } from 'react'; export default function ClientSideSkillList() { const [skills, setSkills] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { const fetchSkills = async () => { const res = await fetch('https://www.dnd5eapi.co/api/skills'); const data = await res.json(); setSkills(data.results); setLoading(false); }; fetchSkills(); }, []); if (loading) return <div>加载中...</div>; return ( <ul> {skills.map((skill) => ( <li key={skill.index} onClick={() => console.log(skill.desc)} style={{ cursor: 'pointer' }} > {skill.name} </li> ))} </ul> ); }
用SWR实现(Next.js推荐的客户端数据获取方案)
"use client"; import useSWR from 'swr'; // 通用请求函数 const fetcher = async (url) => { const res = await fetch(url); return res.json(); }; export default function SWRBasedSkillList() { const { data, error, isLoading } = useSWR('https://www.dnd5eapi.co/api/skills', fetcher); if (error) return <div>加载技能失败</div>; if (isLoading) return <div>加载中...</div>; return ( <ul> {data.results.map((skill) => ( <li key={skill.index} onClick={() => console.log(skill.desc)} style={{ cursor: 'pointer' }} > {skill.name} </li> ))} </ul> ); }
内容的提问来源于stack exchange,提问作者Polikuc
相关产品推荐
相关产品推荐

