Next.js App Router服务端组件如何用onClick实现加载更多文章
Next.js服务端组件实现onClick加载更多的最简方案
不需要把整个PostList改成客户端组件——服务端组件的异步数据预取优势可以保留,只需要把交互逻辑部分抽成独立的客户端组件即可,这是最符合Next.js App Router设计理念的方案。
具体实现步骤
1. 改造原服务端组件PostList
让它接收numOfPosts作为props,不再硬写固定数值:
export async function getData(category: string, numOfPosts: number) { const res = await getPostsByCategory(category, numOfPosts); if (!res) { throw new Error("Failed to fetch data"); } return res; } // 依然是服务端组件,负责数据预取和列表渲染 const PostList = async ({ category, numOfPosts }: { category: string; numOfPosts: number }) => { const data = await getData(category, numOfPosts); return ( <div> {data.map((item: any, idx: number) => { return ( <div key={idx} className={`${styles.grid2} ${categoryStyles.categoryPaddingBottom}`} > <Link href={`/${category}/${item.slug}`}> <div className={styles.relativeEle}> <Image src={item.featuredImage.node.sourceUrl} alt={item.title} sizes="500px" fill priority={true} /> <div className={styles.hoverOverlay}></div> </div> </Link> <div className={categoryStyles.flexCategory}> <Link href={`/${category}/${item.slug}`}>{item.title}</Link> <span>{item.date}</span> <div className={styles.dangerHtml} dangerouslySetInnerHTML={{ __html: item?.content.slice(0, 190) + " ...", }} ></div> </div> </div> ); })} </div> ); }; export default PostList;
2. 创建客户端交互组件LoadMoreController
用'use client'标记,负责维护状态和处理点击事件:
'use client'; import { useState } from 'react'; import PostList from './PostList'; export default function LoadMoreController({ category }) { // 维护展示的文章数量状态 const [numOfPosts, setNumOfPosts] = useState(2); const handleLoadMore = () => { setNumOfPosts(prev => prev + 2); // 每次点击增加2篇,可按需调整 }; return ( <div> {/* 将状态传递给服务端组件,状态变化时会触发服务端重新取数渲染 */} <PostList category={category} numOfPosts={numOfPosts} /> <button onClick={handleLoadMore}>加载更多</button> </div> ); }
3. 在页面中使用客户端控制器
替换原来直接引用PostList的方式:
// 页面组件(可服务端可客户端) import LoadMoreController from './LoadMoreController'; export default function CategoryPage({ params }) { return <LoadMoreController category={params.category} />; }
方案优势
- 保留服务端组件的核心价值:首屏数据在服务端预取,更快的渲染速度和更好的SEO表现。
- 仅将必要的交互逻辑放在客户端组件,避免全客户端化导致的性能损耗。
- 状态变化时,服务端组件会自动重新执行
getData并渲染新的列表,无需手动处理数据拼接。
备选方案(全客户端组件)
如果你的场景不需要服务端预取数据,也可以直接把PostList改成客户端组件,用useState+useEffect管理数据:
'use client'; import { useState, useEffect } from 'react'; // 导入其他依赖... export default function PostList({ category }) { const [posts, setPosts] = useState([]); const [numOfPosts, setNumOfPosts] = useState(2); const [isLoading, setIsLoading] = useState(false); useEffect(() => { const fetchPosts = async () => { setIsLoading(true); try { // 调用API接口获取数据(需自行实现API路由) const res = await fetch(`/api/posts/${category}?num=${numOfPosts}`); const data = await res.json(); setPosts(data); } catch (err) { console.error('获取文章失败', err); } finally { setIsLoading(false); } }; fetchPosts(); }, [category, numOfPosts]); const handleLoadMore = () => { setNumOfPosts(prev => prev + 2); }; // 渲染列表和按钮... }
内容的提问来源于stack exchange,提问作者Vladimir
相关产品推荐
相关产品推荐

