Next.js中如何将异步API获取的结果列表渲染到组件?
问题解决:异步数据传入Next.js组件的正确方式
核心问题
你直接把getStaticRecycleKPI()的返回值传给了TestGrid的resultList,但async函数的返回值是Promise对象,不是实际的数组。组件里用Array.from(resultList)遍历Promise自然拿不到数据,所以控制台打印不出列表项。
解决方案
针对Next.js项目,有两种标准方案解决异步数据传递问题:
方案一:使用Next.js的getStaticProps(推荐,静态生成场景)
Next.js的getStaticProps会在构建时异步获取数据,然后作为props传给页面组件,这样页面拿到的就是实际的数组,再传给TestGrid即可。
修改index.tsx代码:
import { GetStaticProps } from 'next'; import TestGrid from './path-to-your-testgrid-component'; // 替换成实际组件路径 async function getStaticRecycleKPI() { const notion = new Client({ auth: "....", }); const databaseId = "...."; const response = await notion.databases.query({ database_id: databaseId, }); const resultList: REcycleClass[] = []; response.results.forEach((employee: any) => { resultList.push( new REcycleClass( employee.properties.Title.title[0].plain_text, employee.properties.Number.number, employee.properties.Unit.rich_text[0].plain_text ) ); }); return resultList; } class REcycleClass { title: string; figure: number; unit: string; constructor(name: string, amount: number, unit: string) { this.title = name; this.figure = amount; this.unit = unit; } } // 页面组件,接收getStaticProps返回的数据 export default function Home({ recyclerKPI }) { return ( <section id="recycle_kpi_content"> <TestGrid appliedStyle="gchhkgrid3" itemStyle="squaredark" resultList={recyclerKPI} /> </section> ); } // 定义getStaticProps,异步获取数据并传给页面 export const getStaticProps: GetStaticProps = async () => { const recyclerKPI = await getStaticRecycleKPI(); return { props: { recyclerKPI, }, // 可选:设置重新生成时间(秒),用于增量静态再生 // revalidate: 60, }; };
方案二:在页面组件内用useState+useEffect获取数据
如果需要在客户端动态获取数据(比如数据频繁更新),可以在页面组件内用React Hooks处理异步请求:
修改index.tsx的页面组件部分:
import { useState, useEffect } from 'react'; import TestGrid from './path-to-your-testgrid-component'; // ... 保留getStaticRecycleKPI和REcycleClass的定义 ... export default function Home() { const [recyclerKPI, setRecyclerKPI] = useState<REcycleClass[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { async function fetchData() { const data = await getStaticRecycleKPI(); setRecyclerKPI(data); setLoading(false); } fetchData(); }, []); // 加载状态下可以显示占位符 if (loading) { return <div>加载中...</div>; } return ( <section id="recycle_kpi_content"> <TestGrid appliedStyle="gchhkgrid3" itemStyle="squaredark" resultList={recyclerKPI} /> </section> ); }
同步修正TestGrid组件的小问题
- React中元素的类名要用
className,你代码里的<div class={appliedStyle}>要改成<div className={appliedStyle}> useEffect要添加依赖项,避免不必要的重复执行;用map替代forEach生成JSX是React常规写法,同时要给列表项加唯一key
修改后的TestGrid组件:
import { useEffect } from 'react'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import styles from './your-styles-module.module.css'; // 替换成实际样式路径 const TestGrid = ({ appliedStyle, itemStyle, resultList }) => { useEffect(() => { console.log(resultList); }, [resultList]); // 添加resultList作为依赖,仅数据变化时执行 const jsx = resultList.map((cardItem, index) => ( <div className={itemStyle} key={index}> <Container className={styles.container_item_1}> <Typography variant="h2" color="white" align="center"> {cardItem.title} </Typography> {typeof cardItem.figure !== "undefined" && typeof cardItem.unit !== "undefined" && ( <Typography variant="h2" color="white" align="center"> {cardItem.figure + " " + cardItem.unit} </Typography> )} <br /> </Container> </div> )); return <div className={appliedStyle}>{jsx}</div>; }; export default TestGrid;
说明
- 方案一适合数据不频繁更新的场景,构建时生成静态页面,性能更好;
- 方案二适合需要客户端动态获取最新数据的场景;
- 用
map生成JSX时添加key,可以避免React渲染警告。
内容的提问来源于stack exchange,提问作者Jeff Bootsholz
相关产品推荐
相关产品推荐

