React服务端组件下,如何获取最高卡片高度并统一设置其他卡片?
解决服务端组件中卡片等高问题
原代码的核心问题
- 服务端组件限制:你使用了
useState、useEffect这类客户端Hook,还直接操作document对象,但服务端组件(Server Component)不支持这些浏览器/客户端专属API,代码在服务端渲染阶段会直接报错。 - 逻辑缺陷:初始化
maxHeight为undefined,导致第一次遍历卡片时,if (maxHeight)条件不成立,根本不会执行高度比较,最终maxHeight始终是undefined,无法生效。 - 选择器无效:卡片元素没有添加
.card类,document.querySelectorAll(".card")无法选中任何元素。
推荐方案:纯CSS实现(无JS,适配服务端组件)
既然你已经用了Grid布局,直接利用Grid的特性就能实现所有卡片等高,不需要任何JS代码,性能最优且适配服务端组件。
修改步骤:
- 给Grid容器添加
auto-rows-fr类(对应CSS的grid-auto-rows: 1fr),让所有行的高度统一为最高行的高度。 - 给每个卡片添加
h-full类,确保卡片填满Grid单元格的高度。
修改后的代码:
// 这是服务端组件,无需任何客户端Hook import Link from 'next/link'; export default function BestPracticesSection({ BestPractices }) { return ( <div className="grid grid-cols-1 gap-6 px-2 mx-auto md:grid-cols-3 auto-rows-fr"> {BestPractices.map((bestPractice) => ( <Link href={bestPractice.href} key={bestPractice.name}> <div className="relative p-6 h-full transition-all ease-in-out border rounded-lg drop-shadow-card duration-120 hover:border-brand-dark border-slate-100 bg-slate-100 hover:scale-105 hover:cursor-pointer dark:border-slate-600 dark:bg-slate-800"> <div className={clsx( "absolute right-6 rounded-full px-3 py-1 text-xs lg:text-sm", bestPractice.category === "Boost Retention" && "bg-pink-100 text-pink-500 dark:bg-pink-800 dark:text-pink-200", bestPractice.category === "Increase Revenue" && "bg-blue-100 text-blue-500 dark:bg-blue-800 dark:text-blue-200", bestPractice.category === "Understand Users" && "bg-orange-100 text-orange-500 dark:bg-orange-800 dark:text-orange-200" )}> {bestPractice.category} </div> <div className="w-12 h-12"> <bestPractice.icon className="w-12 h-12 " /> </div> <h3 className="mt-3 mb-1 text-xl font-bold text-slate-700 dark:text-slate-200"> {bestPractice.name} </h3> <p className="text-sm text-slate-600 dark:text-slate-400">{bestPractice.description}</p> </div> </Link> ))} </div> ); }
备选方案:客户端组件JS计算(需转客户端组件)
如果必须用JS计算高度,需要将卡片模块转为客户端组件(添加'use client'指令),并修正逻辑:
修改后的客户端组件代码:
'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; export default function BestPracticesCards({ BestPractices }) { const [maxHeight, setMaxHeight] = useState(0); // 计算所有卡片的最大高度 const calculateMaxHeight = () => { const cards = document.querySelectorAll(".card"); let currentMax = 0; cards.forEach(card => { const height = card.getBoundingClientRect().height; if (height > currentMax) currentMax = height; }); setMaxHeight(currentMax); }; useEffect(() => { // 首次渲染后计算 calculateMaxHeight(); // 窗口 resize 时重新计算 window.addEventListener('resize', calculateMaxHeight); // 清理副作用 return () => window.removeEventListener('resize', calculateMaxHeight); }, [BestPractices]); // 数据变化时重新计算 return ( <div className="grid grid-cols-1 gap-6 px-2 mx-auto md:grid-cols-3"> {BestPractices.map((bestPractice) => ( <Link href={bestPractice.href} key={bestPractice.name}> <div style={{ height: `${maxHeight}px` }} className="card relative p-6 transition-all ease-in-out border rounded-lg drop-shadow-card duration-120 hover:border-brand-dark border-slate-100 bg-slate-100 hover:scale-105 hover:cursor-pointer dark:border-slate-600 dark:bg-slate-800"> {/* 内部内容不变 */} <div className={clsx( "absolute right-6 rounded-full px-3 py-1 text-xs lg:text-sm", bestPractice.category === "Boost Retention" && "bg-pink-100 text-pink-500 dark:bg-pink-800 dark:text-pink-200", bestPractice.category === "Increase Revenue" && "bg-blue-100 text-blue-500 dark:bg-blue-800 dark:text-blue-200", bestPractice.category === "Understand Users" && "bg-orange-100 text-orange-500 dark:bg-orange-800 dark:text-orange-200" )}> {bestPractice.category} </div> <div className="w-12 h-12"> <bestPractice.icon className="w-12 h-12 " /> </div> <h3 className="mt-3 mb-1 text-xl font-bold text-slate-700 dark:text-slate-200"> {bestPractice.name} </h3> <p className="text-sm text-slate-600 dark:text-slate-400">{bestPractice.description}</p> </div> </Link> ))} </div> ); }
使用方式:
在服务端组件中导入这个客户端组件,传入BestPractices数据即可:
// 服务端组件 import BestPracticesCards from './BestPracticesCards'; export default function Page() { // 假设这里获取BestPractices数据 const BestPractices = [...] return <BestPracticesCards BestPractices={BestPractices} />; }
内容的提问来源于stack exchange,提问作者hey
相关产品推荐
相关产品推荐

