You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过{children}语法将Client组件Props传递给Server组件?

解决Next.js中Client组件向作为children的Server组件传递状态的问题

Server组件运行在服务器端,无法直接接收客户端的动态state,必须通过能传递到服务器的方式传递数据。最直接且符合Next.js设计的方案是利用URL搜索参数(SearchParams)同步客户端状态,让Server组件从searchParams中读取参数。

方案实现步骤

1. 修改Client组件(BookingsWrapper),同步状态到URL参数

引入useRouter和useSearchParams,将date和searchTerm的状态变化同步到URL查询参数中:

"use client"

import React, { useState, useEffect } from "react"
import { useRouter, useSearchParams } from "next/navigation"

const BookingsWrapper = ({ children }) => {
  const router = useRouter()
  const searchParams = useSearchParams()
  
  // 从URL参数初始化状态,保证刷新页面后状态不丢失
  const [date, setDate] = useState(searchParams.get("date") || "")
  const [searchTerm, setSearchTerm] = useState(searchParams.get("searchTerm") || "")

  // 状态变化时更新URL参数
  useEffect(() => {
    const params = new URLSearchParams()
    if (date) params.set("date", date)
    if (searchTerm) params.set("searchTerm", searchTerm)
    // 使用replace避免历史记录堆积
    router.replace(`?${params.toString()}`, { scroll: false })
  }, [date, searchTerm, router])

  return (
    <div className="flex">
      <div className="w-2/12 bg-gray-100 h-screen">
        <ul className="p-4">
          <li className="mb-2">
            <div className="mb-4">
              <label className="block text-gray-700 text-sm font-bold mb-2" htmlFor="date">Date:</label>
              <input
                value={date}
                onChange={(e) => setDate(e.target.value)}
                className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" 
                id="date" 
                type="date" 
              />
            </div>
          </li>
          <li className="mb-2">
            <input 
              value={searchTerm} 
              onChange={(e) => setSearchTerm(e.target.value)} 
              className="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" 
              id="searchTerm" 
              type="text" 
              placeholder="Search..."
            />
          </li>
        </ul>
      </div>
      {children}
    </div>
  )
}

export default BookingsWrapper

2. 修改Server组件(AllBookingsComponent),从searchParams读取参数

让Server组件接收searchParams参数,从中提取date和searchTerm,并用于数据过滤:

import { authOptions } from "@app/api/auth/[...nextauth]/route";
import ReservationPreview from "@components/ReservationPreview";
import { getServerSession } from "next-auth";
import { redirect } from "next/navigation";

export default async function AllBookingsComponent({ searchParams }) {
  const session = await getServerSession(authOptions);
  
  if (!session) {
    redirect('/');
  }

  // 从URL参数中提取筛选条件
  const date = searchParams.get("date") || "";
  const searchTerm = searchParams.get("searchTerm") || "";

  const getReservations = async () => {
    try {
      // 推荐:将筛选参数传给后端,让后端做数据过滤,减少前端数据量
      const url = new URL("some url...");
      if (date) url.searchParams.set("date", date);
      if (searchTerm) url.searchParams.set("searchTerm", searchTerm);

      const res = await fetch(url.toString(), {
        headers: {
          'Authorization': `Bearer ${session?.user?.accessToken}`, 
        }
      });

      if (!res.ok) {
        throw new Error("Failed to fetch reservations");
      }

      return res.json();
    } catch (error) {
      console.log("Error loading reservations: ", error);
      return { allReservations: [] };
    }
  };

  const { allReservations } = await getReservations();

  // 后端未做过滤时,在前端处理筛选逻辑
  let filteredReservations = allReservations;
  
  if (date) {
    filteredReservations = filteredReservations.filter(r => r.date === date);
  }

  if (searchTerm) {
    const lowerSearch = searchTerm.toLowerCase();
    filteredReservations = filteredReservations.filter(r => 
      r.name.toLowerCase().includes(lowerSearch) || 
      r.email.toLowerCase().includes(lowerSearch)
    );
  }

  return (
    <div className="flex w-screen">
      <div className="w-10/12">
        <div className="container mx-auto my-auto px-4">
          <div className="flex flex-wrap">
            {filteredReservations?.map((r) => (
              <div key={r._id} className="w-full md:w-1/2 lg:w-1/3">
                <ReservationPreview reservation={r} />
              </div>
            ))}
          </div>
        </div>
      </div>
    </div> 
  )
}

3. 页面组件保持原样

无需修改页面组件的结构,继续使用BookingsWrapper包裹AllBookingsComponent即可:

import AllBookingsComponent from '@components/AllBookingsComponent'
import BookingsWrapper from '@components/BookingsWrapper'
import React from 'react'

const Page = () => {
  return (
    <BookingsWrapper>
      <AllBookingsComponent />
    </BookingsWrapper>
  )
}

export default Page

方案优势

  • 完全符合Next.js组件模型,Server组件通过官方支持的searchParams获取参数
  • 状态与URL同步,支持页面刷新、分享链接,用户筛选状态不会丢失
  • 实现简单,无需额外状态管理库

内容的提问来源于stack exchange,提问作者nedzad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:04:51