"scrollIntoView cannot read properties of 'null'" error in React

Issue

I am receiving the error Uncaught TypeError: Cannot read properties of null (reading ‘scrollIntoView’) when trying to click on an item on my Navbar.

It’s bit of a tricky issue, but if I load the page from scratch, it will throw the above error. However, if I save the project in VSCode again, it will work perfectly fine until I refresh the page. Is there anyway to correct this issue?

Navbar.jsx:

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

function Navbar() {
    const aboutPage = document.getElementById('about');
    const home = document.getElementById('home');
    const skills = document.getElementById('skills');
    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={() =>{home.scrollIntoView({behavior: 'smooth'})}}>Home</li>
                <li onClick={() =>{aboutPage.scrollIntoView({behavior: 'smooth'})}}>About</li>
                <li onClick={() => {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;

App.js:

import { useState, useEffect } from 'react';
import { Route, Routes } from 'react-router-dom';
import TextTransition, { presets } from 'react-text-transition';
import './App.css';
import Navbar from './components/Navbar';
import Homepage from './components/Homepage';
import About from './components/About';
import Skills from './components/Skills';
import myAvatar from './images/avataaars.png';

function App() {

  return (
    <>
    <Navbar />
      <div className='homepage-container-web'>
        <Homepage />
        <About />
        <Skills />
      </div>
    <div className='homepage-container-devices'>
      <Routes>
        <Route path='/' element={<Homepage />} />
        <Route path='/about' element={<About />}/>
      </Routes>
    </div>
    </>
  );
}

export default App;

About.jsx:

import '../App.css';

function About() {
    return(
        <>
        <div className='about-container' id='about'>
            <h2 class='about-title'>A little bit about me...</h2>
            <p>Hi, my name is Andrew! I'm 20 years old, and I have <a href='https://www.crohnscolitisfoundation.org/what-is-crohns-disease' target="_blank" style={{color: '#a61bee'}}>Crohn's Disease</a>. Ever since I was 12 years old I have had an interest in technology, software engineering, cybersecurity, firefighting, and cars. I currently work for the Department of Defense and hold a Senior IT Specialist position. I am always looking to learn and improve myself.</p>
        </div>
        <div className='about-ending-container' />
        </>
    )
}

export default About;

Solution

The issue is that the first time it renders, when you do this

const aboutPage = document.getElementById('about');
const home = document.getElementById('home');
const skills = document.getElementById('skills');

those elements don’t exist yet. The simplest solution would be to just change it to

const aboutPage = () => document.getElementById('about');
const home = () => document.getElementById('home');
const skills = () => document.getElementById('skills');

// ...

<li onClick={() =>{home().scrollIntoView({behavior: 'smooth'})}}>Home</li>
<li onClick={() =>{aboutPage().scrollIntoView({behavior: 'smooth'})}}>About</li>
<li onClick={() => {skills().scrollIntoView({behavior: 'smooth'})}}>Skills</li>

so that it does the search at the time of the click. That’s not a very "react" way of doing it. But it should "just work".

If you want to do it the "right way", I supposed you’d want to use useRef, and hoist that up to the App. Using just home as an example, and omiting non-relevant code, it would be something like:

function App() {
    const homeRef = useRef();
    // ...
    <Navbar homeRef={homeRef} />
    // ...
    <Homepage ref={homeRef} />
}

function Navbar({ homeRef }) {
    const scrollIntoView = (ref) => {
        if (ref.current) {
            ref.current.scrollIntoView({behavior: 'smooth'})
        }
    }
    
    <li onClick={() =>{scrollIntoView(homeRef)}}>Home</li>
} 

const Homepage = React.forwardRef((props, ref) => {
    return(
        <>
            <div className='home-container' id='hone' ref={ref} >
                //...
            </div>
        </>
    )
})

Answered By – dave

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published