动态数据引发页面滚动导航首次定位异常问题咨询
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
相关产品推荐
相关产品推荐

