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

如何在React TypeScript中用useRef+react-scroll实现单页平滑滚动

单页React顶部导航栏平滑滚动实现问题

我正在开发一款单页网站,带有顶部导航栏,功能类似顶部导航平滑滚动的示例网站,但采用React + TypeScript实现。目前已编写NavBar.tsx和App.tsx,其中NavBar.tsx代码如下:

import { Button, Link } from 'react-scroll';
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';
import { useEffect } from 'react';
import { useRef } from 'react';

export interface NavBarProps {
    events?: string;
    team?: string;
    sponsors?: string;
    contact?: string;
    corporate?: string;
}

export default function NavBar(props: NavBarProps) {

    let myRef: Array<HTMLDivElement> = [];

    const saveRef = (ref: HTMLDivElement) => {
        myRef.push(ref);
    }

    return (
        <ul className="w-full sticky top-0">
            <li>
                <Link className="link inline float-right p-5 hover:bg-yellow-400 text-white" id="navBar"
                    activeClass="active" to={props.events} spy={true} smooth={true} offset={-70} duration={500}
                    ref={saveRef}>
                    Events
                </Link>
                <div ref={saveRef}>
                    <Link className="link inline float-right p-5 hover:bg-yellow-400 text-white" id="navBar"
                        activeClass="active" to="projects" spy={true} smooth={true} offset={-70} duration={500}
                        ref={saveRef}>
                        Our Team
                    </Link>
                </div>
                <div ref={saveRef}>
                    <Link className="link inline float-right p-5 hover:bg-yellow-400 text-white" id="navBar"
                        activeClass="active" to="blog" spy={true} smooth={true} offset={-70} duration={500}
                        ref={saveRef}>
                        Sponsors
                    </Link>
                </div>
                <div ref={saveRef}>
                    <Link className="link inline float-right p-5 hover:bg-yellow-400 text-white" id="navBar"
                        activeClass="active" to="contact" spy={true} smooth={true} offset={-70} duration={500}
                        ref={saveRef}>
                        Contact Us
                    </Link>
                </div>
                <div ref={myR}>
                    <Link className="link inline float-right p-5 hover:bg-yellow-400 text-white" id="navBar"
                        activeClass="active" to="section1" spy={true} smooth={true} offset={-70} duration={500}
                        >
                        Corporate
                    </Link>
                </div>
            </li>
        </ul>
    )
}

(当前为测试版本,部分链接信息不一致;类名较长因使用Tailwind CSS)

我原本用react-scroll的Link实现导航跳转,但不确定如何修改以实现类似示例的平滑滚动效果(导航栏固定在顶部)。App.tsx包含4个导航指向的区块,虽参考过相关教程,但不清楚如何适配React TypeScript。已知react-scroll可实现平滑滚动,但不确定如何与react-router-dom的Link配合,也不知如何正确用useRef获取元素完成该功能。


解决方案

一、修复react-scroll配置实现平滑滚动

你的代码已经配置了smooth={true},但需修正几个问题并确保目标区块匹配:

  1. 移除重复ID:HTML中id必须唯一,删除所有重复的id="navBar"
  2. 修正ref错误:代码中myR未定义,且saveRef会不断向数组push冗余ref,无需给react-scroll的Link或外层div绑定ref
  3. 统一目标ID匹配:确保Link的to属性值与App.tsx中对应区块的id完全一致

修正后的NavBar.tsx:

import { Link } from 'react-scroll';
import React from 'react';
import { Link as RouterLink } from 'react-router-dom';

export interface NavBarProps {
    events?: string;
    team?: string;
    sponsors?: string;
    contact?: string;
    corporate?: string;
}

export default function NavBar(props: NavBarProps) {
    return (
        <ul className="w-full sticky top-0 bg-white z-50"> {/* 添加背景和层级确保导航栏置顶 */}
            <li className="flex justify-end"> {/* 用Flex替代float,布局更稳定 */}
                <Link 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    activeClass="active" 
                    to={props.events || ''} 
                    spy={true} 
                    smooth={true} 
                    offset={-70} 
                    duration={500}
                >
                    Events
                </Link>
                <Link 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    activeClass="active" 
                    to={props.team || 'projects'} 
                    spy={true} 
                    smooth={true} 
                    offset={-70} 
                    duration={500}
                >
                    Our Team
                </Link>
                <Link 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    activeClass="active" 
                    to={props.sponsors || 'blog'} 
                    spy={true} 
                    smooth={true} 
                    offset={-70} 
                    duration={500}
                >
                    Sponsors
                </Link>
                <Link 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    activeClass="active" 
                    to={props.contact || 'contact'} 
                    spy={true} 
                    smooth={true} 
                    offset={-70} 
                    duration={500}
                >
                    Contact Us
                </Link>
                <Link 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    activeClass="active" 
                    to={props.corporate || 'section1'} 
                    spy={true} 
                    smooth={true} 
                    offset={-70} 
                    duration={500}
                >
                    Corporate
                </Link>
            </li>
        </ul>
    )
}

二、App.tsx配置目标区块

确保每个需要滚动到的区块都设置对应id:

import NavBar from './NavBar';

function App() {
    return (
        <div className="app">
            <NavBar 
                events="events-section"
                team="team-section"
                sponsors="sponsors-section"
                contact="contact-section"
                corporate="corporate-section"
            />
            {/* 目标滚动区块 */}
            <div id="events-section" className="min-h-screen">Events Content</div>
            <div id="team-section" className="min-h-screen">Our Team Content</div>
            <div id="sponsors-section" className="min-h-screen">Sponsors Content</div>
            <div id="contact-section" className="min-h-screen">Contact Us Content</div>
            <div id="corporate-section" className="min-h-screen">Corporate Content</div>
        </div>
    );
}

export default App;

三、react-scroll与react-router-dom配合使用

区分单页滚动和跨页面跳转场景:

  • 单页内滚动:使用react-scroll的Link
  • 跨页面路由跳转:使用react-router-dom的RouterLink

示例:

// 在NavBar中添加首页路由跳转
<RouterLink to="/" className="link p-5 hover:bg-yellow-400 text-white">
    Home
</RouterLink>

四、手动用useRef实现平滑滚动(可选)

若不想依赖react-scroll,可手动实现:

  1. App.tsx中定义ref和滚动方法:
import { useRef } from 'react';
import NavBar from './NavBar';

function App() {
    const eventsRef = useRef<HTMLDivElement>(null);
    const teamRef = useRef<HTMLDivElement>(null);

    const scrollToSection = (ref: React.RefObject<HTMLDivElement>) => {
        ref.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    return (
        <div className="app">
            <NavBar 
                onScrollToEvents={() => scrollToSection(eventsRef)}
                onScrollToTeam={() => scrollToSection(teamRef)}
                // 其他区块的回调...
            />
            <div ref={eventsRef} id="events-section" className="min-h-screen">Events Content</div>
            <div ref={teamRef} id="team-section" className="min-h-screen">Our Team Content</div>
            {/* 其他区块 */}
        </div>
    );
}
  1. 更新NavBarProps和组件:
export interface NavBarProps {
    onScrollToEvents?: () => void;
    onScrollToTeam?: () => void;
    // 其他回调...
}

export default function NavBar(props: NavBarProps) {
    return (
        <ul className="w-full sticky top-0 bg-white z-50">
            <li className="flex justify-end">
                <button 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    onClick={props.onScrollToEvents}
                >
                    Events
                </button>
                <button 
                    className="link p-5 hover:bg-yellow-400 text-white"
                    onClick={props.onScrollToTeam}
                >
                    Our Team
                </button>
                {/* 其他按钮 */}
            </li>
        </ul>
    )
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:10:33