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

React服务端组件下,如何获取最高卡片高度并统一设置其他卡片?

解决服务端组件中卡片等高问题

原代码的核心问题

  1. 服务端组件限制:你使用了useState、useEffect这类客户端Hook,还直接操作document对象,但服务端组件(Server Component)不支持这些浏览器/客户端专属API,代码在服务端渲染阶段会直接报错。
  2. 逻辑缺陷:初始化maxHeight为undefined,导致第一次遍历卡片时,if (maxHeight)条件不成立,根本不会执行高度比较,最终maxHeight始终是undefined,无法生效。
  3. 选择器无效:卡片元素没有添加.card类,document.querySelectorAll(".card")无法选中任何元素。

推荐方案:纯CSS实现(无JS,适配服务端组件)

既然你已经用了Grid布局,直接利用Grid的特性就能实现所有卡片等高,不需要任何JS代码,性能最优且适配服务端组件。

修改步骤:

  1. 给Grid容器添加auto-rows-fr类(对应CSS的grid-auto-rows: 1fr),让所有行的高度统一为最高行的高度。
  2. 给每个卡片添加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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 14:17:02