如何不使用URL搜索参数实现客户端与服务端组件的搜索交互?
不使用URL搜索参数实现客户端输入传递给服务端组件的方案
服务端组件(Server Component)在服务器端渲染完成后就会发送给客户端,无法直接接收客户端的实时交互状态(比如输入框内容)。要实现需求,需要通过客户端触发服务端数据获取的方式来完成,以下是两种可行方案:
方案一:使用Server Actions(Next.js 13+ 推荐)
无需额外创建API路由,直接在组件内定义服务端动作,客户端调用它获取数据,同时支持输入变化自动触发搜索。
步骤1:定义Server Action
创建单独文件封装服务端数据获取逻辑,标记为服务端执行:
// actions.js 'use server'; export async function fetchData(query) { if (!query.trim()) return null; // 替换为你的服务端数据获取逻辑(比如调用数据库/外部API) const res = await fetch(`https://your-api-domain.com/search?query=${encodeURIComponent(query)}`, { cache: 'no-store', // 禁用缓存,确保每次获取最新结果 }); if (!res.ok) throw new Error('数据获取失败'); return res.json(); }
步骤2:调整组件结构
客户端组件维护输入状态,输入变化时调用Server Action,并将结果传递给ClientDisplayComponent:
// 页面组件(客户端组件) 'use client'; import { useState, useEffect } from 'react'; import { fetchData } from './actions'; import ClientDisplayComponent from './ClientDisplayComponent'; export default function SearchPage() { const [query, setQuery] = useState(''); const [data, setData] = useState(null); const [isLoading, setIsLoading] = useState(false); // 输入防抖,避免频繁请求 useEffect(() => { const timer = setTimeout(async () => { if (!query.trim()) { setData(null); return; } setIsLoading(true); try { const result = await fetchData(query); setData(result); } catch (err) { console.error(err); setData(null); } finally { setIsLoading(false); } }, 300); // 300ms防抖延迟 return () => clearTimeout(timer); }, [query]); return ( <div> <input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="输入搜索内容..." /> {isLoading && <p>加载中...</p>} <ClientDisplayComponent data={data} /> </div> ); } // ClientDisplayComponent.js(保持客户端组件) 'use client'; export default function ClientDisplayComponent({ data }) { if (!data) return <p>请输入搜索内容</p>; return ( <div> <h3>搜索结果</h3> <ul> {data.map(item => ( <li key={item.id}>{item.title}</li> ))} </ul> </div> ); }
方案二:使用API路由 + SWR(适合需要缓存的场景)
创建独立API路由处理数据请求,客户端用SWR库自动管理请求、缓存和重新获取,同样支持输入变化自动更新。
步骤1:创建API路由
// app/api/search/route.js export async function GET(request) { const { searchParams } = new URL(request.url); const query = searchParams.get('query'); if (!query?.trim()) { return new Response(JSON.stringify({ error: '缺少搜索关键词' }), { status: 400 }); } // 服务端数据获取逻辑 const res = await fetch(`https://your-api-domain.com/search?query=${encodeURIComponent(query)}`, { cache: 'no-store', }); if (!res.ok) { return new Response(JSON.stringify({ error: '数据获取失败' }), { status: 500 }); } const data = await res.json(); return new Response(JSON.stringify(data), { status: 200 }); }
步骤2:客户端组件使用SWR
// 页面组件(客户端组件) 'use client'; import { useState } from 'react'; import useSWR from 'swr'; import ClientDisplayComponent from './ClientDisplayComponent'; // 定义请求函数 const fetcher = async (url) => { const res = await fetch(url); if (!res.ok) throw new Error('请求失败'); return res.json(); }; export default function SearchPage() { const [query, setQuery] = useState(''); // SWR自动监听query变化,发起请求并处理缓存 const { data, error, isLoading } = useSWR( query ? `/api/search?query=${encodeURIComponent(query)}` : null, fetcher, { debounce: 300 } // 300ms防抖 ); return ( <div> <input type="text" value={query} onChange={(e) => setQuery(e.target.value)} placeholder="输入搜索内容..." /> {isLoading && <p>加载中...</p>} {error && <p>获取数据失败,请重试</p>} <ClientDisplayComponent data={data} /> </div> ); } // ClientDisplayComponent.js 同方案一
关键说明
- 服务端组件无法直接接收客户端实时状态,必须通过客户端触发服务端数据获取的方式实现交互
- 防抖处理:设置300ms延迟,避免每次输入字符都发起请求,提升性能
ClientDisplayComponent保持客户端组件,直接接收数据完成渲染
内容的提问来源于stack exchange,提问作者Blockt
相关产品推荐
相关产品推荐

