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

React中点击导航栏触发scrollIntoView读取null属性错误

解决Uncaught TypeError: Cannot read properties of null (reading 'scrollIntoView')错误

问题根源

你在Navbar.jsx的组件函数顶部直接调用document.getElementById获取home、about、skills元素,但此时Navbar组件先于Homepage、About、Skills完成渲染,这些元素的DOM还没生成,所以拿到的结果都是null。点击导航项时调用scrollIntoView自然会抛出错误。而VSCode热重载时,页面DOM已经渲染完成,这时候能正常获取元素,所以功能恢复正常,刷新后又回到初始状态,报错重现。

解决方案

方案1:点击时再获取DOM元素(最简单直接)

删掉Navbar.jsx顶部提前获取DOM的代码,把获取元素的逻辑放到点击事件里,确保点击时目标元素已经存在:

import { useState } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import myAvatar from '../images/avataaars.png';

function Navbar() {
    const [navbar, setNavbar] = useState(true);

    const handleNavbarToggle = (e) =>{
        setNavbar(!navbar);
    }

    return(
        <>
        <div className='navbar-container'>
            <Link to='/'><img src={myAvatar} className='nav-logo'/></Link>
            <ul>
                <li onClick={() => {
                    const home = document.getElementById('home');
                    home?.scrollIntoView({behavior: 'smooth'});
                }}>Home</li>
                <li onClick={() => {
                    const aboutPage = document.getElementById('about');
                    aboutPage?.scrollIntoView({behavior: 'smooth'});
                }}>About</li>
                <li onClick={() => {
                    const skills = document.getElementById('skills');
                    skills?.scrollIntoView({behavior: 'smooth'});
                }}>Skills</li>
                <li>Projects</li>
                <li id='navbar-login-btn'>Login</li>
            </ul>
            <div className={`navbar-toggle ${navbar ? '' : 'open'}`} onClick={handleNavbarToggle}>
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
        <div className={`navbar-header ${navbar ? 'navbar-header-hide' : ''}`}>
            <ul>
                <Link className='navbar-header-item' to='/'>Home</Link>
                <Link className='navbar-header-item' to='/about'>About</Link>
                <Link className='navbar-header-item' to='/skills'>Skills</Link>
                <Link className='navbar-header-item' to='/projects'>Projects</Link>
                <Link className='navbar-header-item' to='/login'>Login</Link>
            </ul>
        </div>
        </>
    )
}

export default Navbar;

这里用了可选链操作符?.,就算元素意外不存在,也不会抛出错误,提升了代码健壮性。

方案2:使用React的useRef+useEffect(更符合React范式)

通过useRef存储DOM引用,在useEffect中等待组件挂载完成(DOM渲染完毕)后再获取元素:

import { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import '../App.css';
import myAvatar from '../images/avataaars.png';

function Navbar() {
    const [navbar, setNavbar] = useState(true);
    const homeRef = useRef(null);
    const aboutRef = useRef(null);
    const skillsRef = useRef(null);

    useEffect(() => {
        // 组件挂载后,DOM已渲染完成,此时获取目标元素
        homeRef.current = document.getElementById('home');
        aboutRef.current = document.getElementById('about');
        skillsRef.current = document.getElementById('skills');
    }, []); // 空依赖数组确保只在组件挂载时执行一次

    const handleNavbarToggle = (e) =>{
        setNavbar(!navbar);
    }

    return(
        <>
        <div className='navbar-container'>
            <Link to='/'><img src={myAvatar} className='nav-logo'/></Link>
            <ul>
                <li onClick={() => homeRef.current?.scrollIntoView({behavior: 'smooth'})}>Home</li>
                <li onClick={() => aboutRef.current?.scrollIntoView({behavior: 'smooth'})}>About</li>
                <li onClick={() => skillsRef.current?.scrollIntoView({behavior: 'smooth'})}>Skills</li>
                <li>Projects</li>
                <li id='navbar-login-btn'>Login</li>
            </ul>
            <div className={`navbar-toggle ${navbar ? '' : 'open'}`} onClick={handleNavbarToggle}>
                <span></span>
                <span></span>
                <span></span>
            </div>
        </div>
        <div className={`navbar-header ${navbar ? 'navbar-header-hide' : ''}`}>
            <ul>
                <Link className='navbar-header-item' to='/'>Home</Link>
                <Link className='navbar-header-item' to='/about'>About</Link>
                <Link className='navbar-header-item' to='/skills'>Skills</Link>
                <Link className='navbar-header-item' to='/projects'>Projects</Link>
                <Link className='navbar-header-item' to='/login'>Login</Link>
            </ul>
        </div>
        </>
    )
}

export default Navbar;

额外提示

你的App.js中做了响应式布局:桌面端是单页面滚动(同时渲染三个组件),移动端是路由跳转。如果需要让桌面端的滚动逻辑只在对应屏幕尺寸下生效,可以结合window.innerWidth或者媒体查询来做条件判断,避免移动端触发无效的滚动逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 08:45:32