Next.js14:如何将Server Action的temperature数据传给Server Component
在Next.js 14中传递Server Action数据给Server Component展示
问题场景
需要将create Server Action中获取的temperature值传递给Home Server Component,在首页JSX中展示温度数据。
原代码参考
Home组件(page.tsx)
import Image from "next/image"; import Link from "next/link"; import ProductCard from "./components/ProductCard"; import Search from "./components/Search"; interface latLong { latitude: string; longitude: string; } //server action export async function create(data: string) { console.log(data); if (data != "") { const apiKey = "123456"; const geoLocationRes = await fetch( `http://api.openweathermap.org/geo/1.0/direct?q=${data}&appid=${apiKey}` ); const geoLocation = await geoLocationRes.json(); console.log(geoLocation); const getLocation: latLong = { latitude: geoLocation[0].lat, longitude: geoLocation[0].lat, }; const currentWeatherRes = await fetch( ` https://api.openweathermap.org/data/2.5/weather?lat=${getLocation.latitude}&lon=${getLocation.longitude}&appid=${apiKey}&units={metric}` ); const currentWeather = await currentWeatherRes.json(); console.log(currentWeather); const temperature = currentWeather.main.temp; // console.log(temperature); // console.log("input!!"); } else { console.log("no input value"); } } //server component export default async function Home() { return ( <> <h1>Display Temperature here</h1> <Search /> </> ); }
Search组件
const Search = () => { const [inputVal, setInputVal] = useState(""); const OnClick = (e: React.MouseEvent<HTMLButtonElement>) => { e.preventDefault(); create(inputVal); }; return ( <> <form> <input type="text" onChange={(e) => setInputVal(e.target.value)} /> <button type="button" onClick={OnClick}> click </button> </form> </> ); }
解决方案
Next.js中Server Component运行在服务器端,Server Action是用户触发的异步操作,以下两种方案可实现数据传递:
方案1:重定向+搜索参数(Server Component友好)
通过重定向将数据携带在URL搜索参数中,Server Component读取参数后展示数据,符合SSR逻辑:
- 修改
createServer Action:获取温度后重定向回首页,携带城市和温度参数
import { redirect } from 'next/navigation'; interface latLong { latitude: string; longitude: string; } export async function create(formData: FormData) { const data = formData.get('city') as string; if (data.trim() === "") { redirect('/?error=empty-input'); } try { const apiKey = "123456"; // 获取地理坐标 const geoLocationRes = await fetch( `http://api.openweathermap.org/geo/1.0/direct?q=${encodeURIComponent(data)}&appid=${apiKey}` ); const geoLocation = await geoLocationRes.json(); if (!geoLocation[0]) { redirect('/?error=city-not-found'); } const getLocation: latLong = { latitude: geoLocation[0].lat.toString(), longitude: geoLocation[0].lon.toString(), // 修复原代码中longitude赋值错误 }; // 获取天气数据 const currentWeatherRes = await fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${getLocation.latitude}&lon=${getLocation.longitude}&appid=${apiKey}&units=metric` // 修复units参数格式错误 ); const currentWeather = await currentWeatherRes.json(); const temperature = currentWeather.main.temp; // 重定向携带参数 redirect(`/?city=${encodeURIComponent(data)}&temp=${temperature}`); } catch (err) { redirect('/?error=fetch-failed'); } }
- 修改
HomeServer Component:读取URL搜索参数并展示数据
import Search from "./components/Search"; export default async function Home({ searchParams }: { searchParams: { city?: string; temp?: string; error?: string } }) { const { city, temp, error } = searchParams; return ( <> <h1> {temp ? `当前${city}的温度:${temp}°C` : "请输入城市查询温度"} </h1> {/* 错误提示 */} {error && ( <p style={{ color: 'red' }}> {error === 'empty-input' && '请输入城市名称'} {error === 'city-not-found' && '未找到指定城市'} {error === 'fetch-failed' && '获取数据失败,请稍后重试'} </p> )} <Search /> </> ); }
- 修改
Search组件:使用表单提交触发Server Action(规范写法)
'use client'; import { useState } from 'react'; import { create } from '../page'; const Search = () => { const [inputVal, setInputVal] = useState(""); return ( <form action={create}> <input type="text" name="city" value={inputVal} onChange={(e) => setInputVal(e.target.value)} placeholder="输入城市名称" /> <button type="submit">查询温度</button> </form> ); } export default Search;
方案2:useFormState无刷新展示(客户端交互友好)
通过useFormState钩子接收Server Action返回的结果,在客户端组件中直接展示,无需页面刷新:
- 修改
createServer Action:返回温度数据或错误信息
interface latLong { latitude: string; longitude: string; } export async function create(formData: FormData) { const data = formData.get('city') as string; try { if (data.trim() === "") { return { success: false, error: '请输入城市名称' }; } const apiKey = "123456"; const geoLocationRes = await fetch( `http://api.openweathermap.org/geo/1.0/direct?q=${encodeURIComponent(data)}&appid=${apiKey}` ); const geoLocation = await geoLocationRes.json(); if (!geoLocation[0]) { return { success: false, error: '未找到指定城市' }; } const getLocation: latLong = { latitude: geoLocation[0].lat.toString(), longitude: geoLocation[0].lon.toString(), }; const currentWeatherRes = await fetch( `https://api.openweathermap.org/data/2.5/weather?lat=${getLocation.latitude}&lon=${getLocation.longitude}&appid=${apiKey}&units=metric` ); const currentWeather = await currentWeatherRes.json(); const temperature = currentWeather.main.temp; return { success: true, temperature, city: data }; } catch (err) { return { success: false, error: '获取数据失败,请稍后重试' }; } }
- 修改
Search组件:使用useFormState接收结果并展示
'use client'; import { useState, useFormState } from 'react'; import { create } from '../page'; // 初始化状态 const initialState = { success: false, temperature: 0, city: '', error: '' }; const Search = () => { const [inputVal, setInputVal] = useState(""); const [state, formAction] = useFormState(create, initialState); return ( <> <form action={formAction}> <input type="text" name="city" value={inputVal} onChange={(e) => setInputVal(e.target.value)} placeholder="输入城市名称" /> <button type="submit">查询温度</button> </form> {/* 展示温度结果 */} {state.success && ( <h2 style={{ marginTop: '1rem' }}> 当前{state.city}的温度:{state.temperature}°C </h2> )} {/* 错误提示 */} {state.error && ( <p style={{ color: 'red', marginTop: '1rem' }}> {state.error} </p> )} </> ); } export default Search;
Home组件无需修改:温度结果直接在Search组件内展示,若需在Home中统一展示,可将Home改为客户端组件或新增客户端组件负责展示。
内容的提问来源于stack exchange,提问作者Abdullahi Abbas
相关产品推荐
相关产品推荐

