Skip to content
Advertisement

How to use useRef hook properly in react Functional Component?

I have a JavaScript snippet and I’m trying to implement the same in react. Here is the snippet.

const nav = document.querySelector('.nav');
window.addEventListener('scroll', fixNav);

function fixNav() {
  if (window.scrollY > nav.offsetHeight) {
    nav.classList.add('active');
  } else {
    nav.classList.remove('active');
  }
}

For more details about the snippet, please refer this codepen. When I scroll I’m just adding a class to the an element and removing when a certain condition is met using scroll eventListerner. Here is how I tried in react with the help of this official documentation.

const Navbar = () => {
    const navbar = useRef(null)
    window.addEventListener('scroll', () => {
        if (window.scrollY > navbar.current.offsetHeight + 550) {
            navbar.current.classList.add('active');
        }else{
            navbar.current.classList.remove('active');
        }
    });
    return (
        <Fragment>
            <nav id='navbar' className="nav" ref={navbar}>
                <div className="container">
                    <h1 className="logo"><a href="/index.html"></a>My Website</h1>
                    <ul>
                        <li><a href="#" className="current">Home</a></li>
                        <li><a href="#">About</a></li>
                        <li><a href="#">Services</a></li>
                        <li><a href="#">Contact</a></li>
                     </ul>
                </div>
            </nav>
            {/* other elements */}
        </Fragment>
    )
}

The error

TypeError: Cannot read property 'offsetHeight' of null

So, the element navbar I’m trying to get is getting null when I actually scroll and the criterion in met. I could see the navbar element in the console when console.log it and the error as well at the same time. I’m sure I’m missing a fundamental concept behind the scenes.

If I’m trying to get the DOM element before the component is mounted, I must use useEffect to handle it properly. I’m new to react and not able to give it a good shot using useEffect hook.

Advertisement

Answer

You should add the event listener inside useEffect hook to make sure you access the ref after it has been assigned the dom element.

const Navbar = () => {
  const navbar = useRef(null);
  useEffect(()=>{
    window.addEventListener("scroll", () => {
      if(navbar.current!==null){
        if (window.scrollY > navbar.current.offsetHeight + 550) {
          navbar.current.classList.add("active");
        } else {
          navbar.current.classList.remove("active");
        }
      }
    });
  },[]);

  return (
    <Fragment>
      <nav id="navbar" className="nav" ref={navbar}>
        <div className="container">
          <h1 className="logo">
            <a href="/index.html"></a>My Website
          </h1>
          <ul>
            <li>
              <a href="#" className="current">
                Home
              </a>
            </li>
            <li>
              <a href="#">About</a>
            </li>
            <li>
              <a href="#">Services</a>
            </li>
            <li>
              <a href="#">Contact</a>
            </li>
          </ul>
        </div>
      </nav>
      {/* other elements */}
    </Fragment>
  );
};
Advertisement