NextJS基于客户端输入实现服务端Fetch的方案咨询
解决方案
方案1:搜索参数+客户端状态双向同步
你之前用查询字符串时输入状态丢失,问题出在没把搜索参数和输入框的本地状态做双向同步。结合useSearchParams和useState就能解决:用户输入时同时更新本地状态和URL搜索参数,路由刷新后从搜索参数恢复输入状态,服务端组件直接读取搜索参数过滤数据。
客户端组件(过滤输入)
'use client'; import { useSearchParams, useRouter } from 'next/navigation'; import { useState, useEffect } from 'react'; export default function DateFilter() { const searchParams = useSearchParams(); const router = useRouter(); // 从URL搜索参数初始化输入状态 const [dateRange, setDateRange] = useState({ start: searchParams.get('startDate') || '', end: searchParams.get('endDate') || '', }); // 监听搜索参数变化,同步到本地状态(比如用户手动修改URL) useEffect(() => { setDateRange({ start: searchParams.get('startDate') || '', end: searchParams.get('endDate') || '', }); }, [searchParams]); const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; const newDateRange = { ...dateRange, [name]: value }; setDateRange(newDateRange); // 更新URL搜索参数,保留现有其他参数 const params = new URLSearchParams(searchParams); params.set(name, value); router.push(`/?${params.toString()}`, { scroll: false }); }; return ( <div> <input type="date" name="startDate" value={dateRange.start} onChange={handleInputChange} /> <input type="date" name="endDate" value={dateRange.end} onChange={handleInputChange} /> </div> ); }
服务端组件(数据获取)
import { headers } from 'next/headers'; export default async function DataPage() { // 从请求头中解析URL参数 const requestUrl = headers().get('next-url'); const searchParams = requestUrl ? new URLSearchParams(requestUrl.split('?')[1]) : new URLSearchParams(); const startDate = searchParams.get('startDate'); const endDate = searchParams.get('endDate'); // 带过滤参数请求数据 const res = await fetch(`https://api.example.com/data?start=${startDate}&end=${endDate}`); const data = await res.json(); return <div>{/* 渲染过滤后的数据 */}</div>; }
优缺点:支持页面刷新、书签分享,符合REST语义;但参数会暴露在URL中,不适合敏感数据。
方案2:客户端用SWR/React Query直接请求API路由
把数据获取逻辑放到客户端组件,用SWR或React Query管理数据状态,用户输入时触发重新请求,无需刷新路由,服务端通过API路由接收过滤参数处理数据。
客户端组件(带数据请求)
'use client'; import useSWR from 'swr'; import { useState } from 'react'; // 封装请求函数 const fetcher = async (url: string) => { const res = await fetch(url); if (!res.ok) throw new Error('请求失败'); return res.json(); }; export default function DateFilterPage() { const [dateRange, setDateRange] = useState({ start: '', end: '' }); // 根据输入状态动态生成请求URL,输入不全时不发起请求 const { data, isLoading, error } = useSWR( dateRange.start && dateRange.end ? `/api/data?start=${dateRange.start}&end=${dateRange.end}` : null, fetcher ); const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; setDateRange(prev => ({ ...prev, [name]: value })); }; return ( <div> <input type="date" name="start" value={dateRange.start} onChange={handleInputChange} /> <input type="date" name="end" value={dateRange.end} onChange={handleInputChange} /> {isLoading && <p>加载中...</p>} {error && <p>加载失败,请重试</p>} {data && <div>{/* 渲染数据 */}</div>} </div> ); }
API路由(服务端处理过滤)
// app/api/data/route.ts export async function GET(request: Request) { const { searchParams } = new URL(request.url); const startDate = searchParams.get('start'); const endDate = searchParams.get('end'); // 这里替换成你的数据过滤逻辑 const filteredData = await fetchFromDatabase(startDate, endDate); return Response.json(filteredData); }
优缺点:无需刷新路由,用户体验流畅;自带缓存、重试、自动重新请求等特性;但数据获取在客户端,无法利用服务端组件的静态生成优势。
方案3:Cookie传递参数(适合非公开场景)
如果不想把过滤参数暴露在URL中,可以将用户输入的日期范围存在Cookie里,服务端组件读取Cookie后过滤数据。
客户端组件(设置Cookie+触发刷新)
'use client'; import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; export default function DateFilter() { const router = useRouter(); // 从Cookie初始化输入状态 const [dateRange, setDateRange] = useState(() => { const cookieStr = document.cookie.split('; ').find(row => row.startsWith('dateRange=')); return cookieStr ? JSON.parse(cookieStr.split('=')[1]) : { start: '', end: '' }; }); const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const { name, value } = e.target; const newDateRange = { ...dateRange, [name]: value }; setDateRange(newDateRange); // 设置会话级Cookie,关闭浏览器自动失效 document.cookie = `dateRange=${JSON.stringify(newDateRange)}; path=/; sameSite=strict; max-age=86400`; // 触发路由刷新,让服务端组件读取新Cookie router.push(window.location.pathname, { scroll: false }); }; return ( <div> <input type="date" name="start" value={dateRange.start} onChange={handleInputChange} /> <input type="date" name="end" value={dateRange.end} onChange={handleInputChange} /> </div> ); }
服务端组件(读取Cookie)
import { cookies } from 'next/headers'; export default async function DataPage() { const cookieStore = cookies(); const dateRangeStr = cookieStore.get('dateRange')?.value; const dateRange = dateRangeStr ? JSON.parse(dateRangeStr) : { start: '', end: '' }; const res = await fetch(`https://api.example.com/data?start=${dateRange.start}&end=${dateRange.end}`); const data = await res.json(); return <div>{/* 渲染数据 */}</div>; }
优缺点:参数不暴露在URL中,适合隐私场景;但Cookie有大小限制(约4KB),且需要手动处理过期和清理逻辑。
内容的提问来源于stack exchange,提问作者ssantos
相关产品推荐
相关产品推荐

