Next.js:如何在前端展示Server Action返回结果及最佳实践
问题:Next.js Server Action返回的数据如何在前端正确展示?
我是Next.js新手,最近尝试使用新的Server Action功能。我创建了一个表单,提交时会触发Server Action从外部API获取图片链接数组,但不知道怎么在前端正确展示返回的结果。我原本想用原生DOM操作更新页面,但感觉不符合Next.js的最佳实践,想找正确的实现方案。
我的相关代码如下:
page.tsx
import Form from "@/components/Form"; export default function Home() { return ( <div className="container mx-auto flex justify-center items-center h-screen"> <Form /> </div> ); }
form.tsx
"use client"; import { requestImg } from "@/actions/formActions"; export const Form = () => { return ( <form className="bg-white p-8 rounded shadow-md" action={async (formData) => { await requestImg(formData); }} > ... 其他内容(比如输入框和提交按钮) ... </form> ); }; export default Form;
formActions.ts
"use server"; export const requestImg = async (FormData: FormData) => { const link = FormData.get("link"); const response = await fetch( `https://<my API link>/get-all-images/`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ link_post: link, }), } ); const result = await response.json(); console.log(result); };
解决方案
方案一:客户端组件用状态管理(推荐)
这是最符合React/Next.js理念的方式:用useState存储图片数组,通过表单action函数接收Server Action返回的结果,再更新状态驱动页面渲染。
第一步:修改Server Action,返回结果
先修改formActions.ts,把获取到的图片数组return出去,同时增加错误处理:
"use server"; export const requestImg = async (formData: FormData) => { const link = formData.get("link"); const response = await fetch( `https://<my API link>/get-all-images/`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ link_post: link, }), } ); // 处理API请求失败的情况 if (!response.ok) { throw new Error("获取图片失败,请稍后重试"); } const result = await response.json(); return result; // 返回图片链接数组 };
第二步:修改客户端表单组件,管理状态并渲染图片
更新form.tsx,加入状态管理和图片渲染逻辑:
"use client"; import { useState } from "react"; import { requestImg } from "@/actions/formActions"; export const Form = () => { const [images, setImages] = useState<string[]>([]); const [isLoading, setIsLoading] = useState(false); return ( <div className="flex flex-col items-center gap-6"> <form className="bg-white p-8 rounded shadow-md" action={async (formData) => { setIsLoading(true); try { // 接收Server Action返回的图片数组 const imageUrls = await requestImg(formData); setImages(imageUrls); } catch (err) { alert(err instanceof Error ? err.message : "未知错误"); } finally { setIsLoading(false); } }} > {/* 表单输入区域 */} <input type="text" name="link" placeholder="输入目标链接" className="w-full mb-4 p-2 border border-gray-300 rounded" required /> <button type="submit" disabled={isLoading} className="w-full p-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:bg-gray-400" > {isLoading ? "加载中..." : "获取图片"} </button> </form> {/* 图片展示区域 */} {isLoading && <p className="text-gray-600">正在加载图片...</p>} {images.length > 0 && ( <div className="mt-4 grid grid-cols-3 gap-4"> {images.map((url, index) => ( <img key={index} src={url} alt={`图片${index + 1}`} className="w-full h-auto rounded shadow" /> ))} </div> )} </div> ); }; export default Form;
方案二:Server Action重定向+服务器组件传递数据
如果需要在服务器组件中处理并渲染结果,可以通过搜索参数传递数据(适合数据量不大的场景):
第一步:修改Server Action,重定向并携带数据
更新formActions.ts:
"use server"; import { redirect } from "next/navigation"; export const requestImg = async (formData: FormData) => { const link = formData.get("link"); const response = await fetch( `https://<my API link>/get-all-images/`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ link_post: link, }), } ); if (!response.ok) { throw new Error("获取图片失败,请稍后重试"); } const result = await response.json(); // 将图片数组转为编码后的字符串,通过搜索参数传递 const encodedImages = encodeURIComponent(JSON.stringify(result)); redirect(`/?images=${encodedImages}`); };
第二步:修改服务器组件,解析参数并渲染图片
更新page.tsx:
import Form from "@/components/Form"; export default function Home({ searchParams }: { searchParams: { images?: string } }) { let images: string[] = []; if (searchParams.images) { try { images = JSON.parse(decodeURIComponent(searchParams.images)); } catch (err) { console.error("解析图片数据失败:", err); } } return ( <div className="container mx-auto flex flex-col justify-center items-center h-screen gap-8"> <Form /> {/* 展示图片 */} {images.length > 0 && ( <div className="grid grid-cols-3 gap-4"> {images.map((url, index) => ( <img key={index} src={url} alt={`图片${index+1}`} className="rounded shadow" /> ))} </div> )} </div> ); }
最佳实践总结
- 优先选择方案一:客户端状态管理更直观,符合React状态驱动渲染的核心思想,避免原生DOM操作带来的维护问题。
- 方案二适合需要在服务器组件中处理结果的场景,但注意搜索参数有长度限制,数据量较大时不适用。
- 始终在Server Action中处理错误并抛出,方便客户端捕获并给用户清晰的反馈。
内容的提问来源于stack exchange,提问作者NicoCaldo
相关产品推荐
相关产品推荐

