Emil
Emil

Reputation: 145

Cannot read property 'scrollIntoView' of null

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

Upvotes: 5

Views: 38951

Answers (4)

Raj Shankar Tiwary
Raj Shankar Tiwary

Reputation: 1

faced the same issue, what worked for me was using scrollIntoView in the callback of a setTimeout with a delay of 1 sec(or less maybe)

  useEffect(() => {
    setTimeout(() => divRef.current.scrollIntoView({ behavior: 'smooth' }), 1000);
  });

Upvotes: 0

Mr. Hedgehog
Mr. Hedgehog

Reputation: 2885

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.

Upvotes: 8

lissettdm
lissettdm

Reputation: 13078

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);
    }
}

Upvotes: 2

YTG
YTG

Reputation: 1058

You're missing the current prop

 reftp?.current.scrollIntoView()

Upvotes: -1

Related Questions