Cannot read property 'scrollIntoView' of null

Viewed 16410

Why isn't this working as expected? Sometimes it works, and sometimes it doesn't - i cannot figure this one out. The piece of code with ScrollIntoView was copied from another js file with another page, and in that one it works just fine? The ID's refTP and refPP are found within div tags in ReferencesPP and ReferencesTP

import React, {useState} from 'react'
import ReferencesPP from './referencesPP'
import ReferencesTP from './referencesTP'
import "./references.css"

function ReferencesPage(){
    const reftp = document.getElementById("refTP");
    const refpp = document.getElementById("refPP");

    const [page, setPage] = useState(false);

    const handleClick = (id) => {
        if(id === 0 && page===true){
            reftp.scrollIntoView({ behavior: "smooth" });
            setPage(false);
        } else if(id === 1 && page===false){
            refpp.scrollIntoView({ behavior: "smooth" });
            setPage(true);
        }
    }

    return(
        <div className="references-main-container">
            <ul>
                <li id="anchor1" onClick={()=>handleClick(0)}></li>
                <li id="anchor2"onClick={()=>handleClick(1)}></li>
            </ul>
            <ReferencesTP />
            <ReferencesPP />
        </div>
    )
}

export default ReferencesPage
3 Answers

It does not work initially because by the time your component function run there is no elements yet. And it works if there were some sequential renders, since at that time there is some elements. However, it is still wrong way to do this. Accessing DOM from react should be considered side effect and you can't have side effects in pure functions (and your component is pure function.

In functional react components you can tackle side effects in few ways. One of this ways (and often is the best way) is hooks. If you want to store some mutable data (as DOM element) it is good to use useRef hook. This way React will set ref to DOM element and it can be passed by reference into your event handler. You still will need to check if element is actually exists, but with getElementById you should do the same.

I made small fiddle for you to look, how you can use refs for your case.

The first time this lines are evaluated to null because the components have not been rendered yet:

   const reftp = document.getElementById("refTP");
   const refpp = document.getElementById("refPP");

Move the element reference to handleClick function:

const handleClick = (id) => {
   const reftp = document.getElementById("refTP");
   const refpp = document.getElementById("refPP");
    if(id === 0 && page===true){
        reftp.scrollIntoView({ behavior: "smooth" });
        setPage(false);
    } else if(id === 1 && page===false){
        refpp.scrollIntoView({ behavior: "smooth" });
        setPage(true);
    }
}

You're missing the current prop

 reftp?.current.scrollIntoView()
Related