javascriptreactjsurlautoscroll

Auto scroll to anchor tag element in react using componentDidUpdate


https://abc1234.com/users#api-doc


    <div className={parent}>
        <div className={child}>
            {someContent}
            <a id="api-doc">Hello API</a>
        </div>
    </div>

I am writing the page in ReactJS. I need to implement a way so that the page should auto-scroll down to the Hash i.e. api-doc but somehow it's not auto-scrolling on page load (very first time).

I tried a workaround which is like this


    componentDidUpdate(){
        let id = this.props.history.location.hash.replace("#", "");
        let element = document.getElementById(id);
        setTimeout(() => {
            if (element) {
                element.scrollIntoViewIfNeeded(true);
            }
        }, 200);
    }

It's working but I am looking for a better way to achieve the same

In other words, I do not want to use setTimeOut.

I added setTimeOut cause I found that document.getElementById(id) is giving null (very first time), I am assuming that somehow componentDidUpdate is running before the entire render. I have also used dangerouslySetInnerHTML .


Solution

  • You are correct that document.getElementById(id) returns null because the element has not yet rendered.

    With React do not use document.getElementById(). React nodes are not real DOM nodes (e.g., text or element nodes) themselves, but a representation of a potential DOM node.

    Instead, use the hook useRef() React documentation

    Create the following component.

    import React, { useRef, useEffect }  from 'react';
    
    const myComponent = (props) => {
    
      const myRef = useRef();
    
      function scrollToComponent() {
        if (window.location.hash === '#api-doc') {
          myRef.current.scrollIntoView();
          myRef.current.focus();
        }
      }
    
      useEffect( () => scrollToComponent(), [] )
    
      return (
        <div ref={myRef} id="api-doc">
           ...
        </div>
      )
    };
    
    export default myComponent;
    

    Using useEffect this way will (only) trigger when the component mounts. (therefore avoiding the issue of the JS running before the component has mounted)

    The function I have named scrollToComponent will check the URL for a hash, if it passes the condition, it will scroll to the ref.

    Remember to import the component to your main file.

    *Note: using Link from React router may be a better solution. (but one I'm not familiar with)