Next.js 14 App Router架构下页面调用API传参后实现复杂交互的问题
Next.js 14 App Router架构下页面调用API传参后实现复杂交互的问题
嗨,我完全懂你现在的困扰——想在Next.js 14的App Router里既保留SSR(服务端渲染)的优势,又能实现帖子的重新获取、增删改这些复杂交互,结果要么没法修改状态,要么存进useState就丢了SSR,view-source里空空如也,确实头疼!我来给你捋几个可行的解决方案,都是实际项目里常用的思路:
一、拆分服务器组件与客户端组件,保留SSR同时接管状态
核心思路是让服务器组件负责拿初始数据,客户端组件负责交互和状态管理,这样既保证了初始页面有SSR内容,又能自由操作状态:
- 先写一个服务器组件(不用加
"use client")作为页面入口,在这里调用API获取初始帖子数据:
// app/post/page.js (服务器组件) import PostList from "./PostList"; async function fetchPosts() { const res = await fetch("你的API地址", { cache: "no-store" }); return res.json(); } export default async function PostPage() { const initialPosts = await fetchPosts(); // 把服务器获取的初始数据传给客户端组件 return <PostList initialPosts={initialPosts} />; }
- 再写一个客户端组件
PostList,用传入的服务器数据初始化useState,之后的交互就全在这里处理:
// app/post/PostList.jsx (客户端组件) "use client"; import { useState } from "react"; import PostItem from "./components/PostItem"; export default function PostList({ initialPosts }) { // 用服务器返回的初始数据初始化状态,这样SSR时页面已有内容 const [posts, setPosts] = useState(initialPosts); const [activeId, setActiveId] = useState(0); // 重新获取数据的函数 const refetchPosts = async () => { const res = await fetch("你的API地址"); const newPosts = await res.json(); setPosts(newPosts); }; // 删除帖子的函数 const deletePost = async (postId) => { // 先调用删除API await fetch(`你的API地址/${postId}`, { method: "DELETE" }); // 更新客户端状态 setPosts(posts.filter(post => post.id !== postId)); }; // 其他增、改操作逻辑类似,都是先调用API再更新状态 return ( <div> <button onClick={refetchPosts}>重新加载帖子</button> {posts.map(post => ( <PostItem key={post.id} post={post} isActive={post.id === activeId} onActive={() => setActiveId(post.id)} onDelete={() => deletePost(post.id)} /> ))} </div> ); }
这样操作下来,初始页面是服务器渲染的,view-source里能看到帖子内容,后续的交互也完全不受影响,状态修改都在客户端组件里完成。
二、关于全局状态的需求
如果需要跨多个组件共享帖子数据,比如在导航栏或者其他页面也能操作帖子,那可以用React Context或者轻量状态管理工具(比如Zustand),但要注意初始状态还是要从服务器组件传递过来:
比如用Context的话,先在根组件(服务器组件)里获取初始数据,再传给Context Provider:
// app/layout.js (服务器组件) import { PostsProvider } from "./PostsContext"; async function fetchPosts() { const res = await fetch("你的API地址"); return res.json(); } export default async function RootLayout({ children }) { const initialPosts = await fetchPosts(); return ( <html> <body> <PostsProvider initialPosts={initialPosts}> {children} </PostsProvider> </body> </html> ); }
然后在客户端组件里消费Context,就能全局操作帖子状态了,同时初始渲染依然是SSR的。
为啥之前的方法会丢SSR?
你之前在客户端组件里用useEffect存数据,是因为客户端组件在浏览器里才会执行useEffect,服务器渲染时这部分逻辑没跑,所以初始页面是空的。而通过服务器组件先拿数据再传给客户端,服务器渲染时就把初始数据塞进页面里了,view-source自然就有内容啦。
备注:内容来源于stack exchange,提问作者sheng
相关产品推荐
相关产品推荐

