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

动态数据引发页面滚动导航首次定位异常问题咨询

React滚动导航异常问题排查与解决

问题现象

  • 页面包含大量动态数据,首次加载或页面更新后,使用react-scroll的Link或原生<a>标签进行滚动导航时,首次点击会出现滚动位置低于预期的情况
  • 首次滚动后导航恢复正常,但页面刷新后问题复现
  • 注释掉动态内容模块(WorkingWithSection)后滚动恢复正常,尝试useLayoutEffect未解决问题

相关组件代码

WorkingWithSection.tsx

import { cardItems } from "@/data";
import React from "react";
import Card from "./Card";
import SectionHeader from "./UI/SectionHeader";

const WorkingWithSection = () => {
  return (
    <section id="working-with" className="mb-28">
      <SectionHeader>WorkingWith</SectionHeader>
      <div className="flex flex-col gap-y-5 md:grid md:grid-cols-2 md:gap-5 lg:grid-cols-3">
        {cardItems.map((item, index) => (
          <Card
            key={item.id}
            icon={item.icon}
            title={item.title}
            description={item.description}
            addClasses={
              index === cardItems.length - 1
                ? "md:col-span-2 lg:order-3"
                : `lg:order-${index === cardItems.length - 2 ? "4" : index}`
            }
          />
        ))}
      </div>
    </section>
  );
};

export default WorkingWithSection;

Card.tsx

import React from "react";

interface CardProps {
  title: string;
  description: string[];
  addClasses: string;
  icon: React.ElementType;
}

const Card: React.FC<CardProps> = ({
  title,
  description,
  addClasses,
  icon: Icon,
}) => {
  return (
    <article
      className={`${addClasses} transition-card group flex flex-col items-center justify-start rounded-10 border-1 border-white-100 bg-transparent px-7 py-14 text-white-100 hover:bg-white-200 hover:fill-black-100 hover:text-black-100`}
    >
      <figure className="mb-6">
        <Icon className="h-16 w-16 fill-white-100 group-hover:fill-black-100" />
      </figure>
      <h3 className="mb-6 inline text-center text-2xl font-bold">{title}</h3>
      <ul className="inline-block list-inside list-disc px-5">
        {description.map((listItem, index) => (
          <li
            key={index}
            className="transition-text list-disc text-gray-300 group-hover:text-black-100"
          >
            {listItem}
          </li>
        ))}
      </ul>
    </article>
  );
};

export default Card;

ButtonNavigation.tsx(平滑滚动组件)

"use client";

import React from "react";
import { Link } from "react-scroll";

interface ButtonNavigationProps {
  navigateTo: string;
  children: React.ReactNode;
}

const ButtonNavigation: React.FC<ButtonNavigationProps> = ({
  navigateTo,
  children,
}) => {
  return (
    <Link
      smooth={true}
      spy={true}
      isDynamic={true}
      to={navigateTo}
      offset={-120}
      duration={650}
      className="flex items-center justify-center gap-x-2 px-7 py-3 font-medium"
    >
      {children}
      <svg
        xmlns="http://www.w3.org/2000/svg"
        height="24"
        viewBox="0 -960 960 960"
        width="24"
        fill="currentColor"
      >
        <path d="m560-240-56-58 142-142H160v-80h486L504-662l56-58 240 240-240 240Z" />
      </svg>
    </Link>
  );
};

export default ButtonNavigation;

问题原因与解决方案

核心原因

动态内容(卡片列表)渲染完成前,react-scroll已提前计算了目标元素的位置。此时页面高度尚未稳定,获取的目标元素坐标不准确,导致首次滚动位置偏差。

解决方案1:延迟滚动计算,确保内容加载完成

修改ButtonNavigation组件,等待DOM完全渲染后再执行滚动逻辑,避免提前计算位置:

"use client";

import React, { useEffect, useState } from "react";
import { scroller } from "react-scroll";

interface ButtonNavigationProps {
  navigateTo: string;
  children: React.ReactNode;
}

const ButtonNavigation: React.FC<ButtonNavigationProps> = ({
  navigateTo,
  children,
}) => {
  const [isContentReady, setIsContentReady] = useState(false);

  useEffect(() => {
    // 等待动态内容渲染完成,可根据实际加载速度调整时长
    const timer = setTimeout(() => setIsContentReady(true), 300);
    return () => clearTimeout(timer);
  }, []);

  const handleScroll = () => {
    if (!isContentReady) return;
    scroller.scrollTo(navigateTo, {
      smooth: true,
      spy: true,
      isDynamic: true,
      offset: -120,
      duration: 650,
    });
  };

  return (
    <button
      onClick={handleScroll}
      className="flex items-center justify-center gap-x-2 px-7 py-3 font-medium"
    >
      {children}
      <svg
        xmlns="http://www.w3.org/2000/svg"
        height="24"
        viewBox="0 -960 960 960"
        width="24"
        fill="currentColor"
      >
        <path d="m560-240-56-58 142-142H160v-80h486L504-662l56-58 240 240-240 240Z" />
      </svg>
    </button>
  );
};

export default ButtonNavigation;

解决方案2:改用原生滚动,实时获取目标位置

绕过react-scroll的预计算逻辑,直接使用原生API获取目标元素的实时位置:

"use client";

import React from "react";

interface ButtonNavigationProps {
  navigateTo: string;
  children: React.ReactNode;
}

const ButtonNavigation: React.FC<ButtonNavigationProps> = ({
  navigateTo,
  children,
}) => {
  const scrollToTarget = () => {
    const target = document.getElementById(navigateTo);
    if (!target) return;
    // 实时计算目标位置,包含offset偏移
    const targetTop = target.getBoundingClientRect().top + window.scrollY - 120;
    window.scrollTo({ top: targetTop, behavior: "smooth" });
  };

  return (
    <button
      onClick={scrollToTarget}
      className="flex items-center justify-center gap-x-2 px-7 py-3 font-medium"
    >
      {children}
      <svg
        xmlns="http://www.w3.org/2000/svg"
        height="24"
        viewBox="0 -960 960 960"
        width="24"
        fill="currentColor"
      >
        <path d="m560-240-56-58 142-142H160v-80h486L504-662l56-58 240 240-240 240Z" />
      </svg>
    </button>
  );
};

export default ButtonNavigation;

解决方案3:确保动态数据就绪后再渲染组件

在父组件中等待cardItems数据完全加载后,再渲染WorkingWithSection,避免页面高度频繁变化:

// 父组件示例
import { cardItems } from "@/data";
import React, { useState, useEffect } from "react";
import WorkingWithSection from "./WorkingWithSection";

const ParentComponent = () => {
  const [dataReady, setDataReady] = useState(false);

  useEffect(() => {
    // 若为异步接口数据,替换为接口请求完成的判断逻辑
    if (cardItems.length > 0) setDataReady(true);
  }, [cardItems]);

  return (
    <div>
      {/* 其他页面内容 */}
      {dataReady && <WorkingWithSection />}
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 11:07:05