Home > front end >  TypeError: Cannot read properties of null (reading 'classList') React
TypeError: Cannot read properties of null (reading 'classList') React

Time:09-29

I am having this header which on scroll, I want to change the background to a different color. The navbar variable returns null always. Why is it so? How can I do that? The transform:translateY is just for a small animation on scroll.

import React from 'react'
import { Navbar, Container, Nav } from 'react-bootstrap'

import { LinkContainer } from 'react-router-bootstrap'

const Header = () => {

    const navbar = document.getElementById("navbar");
    let scrolled = false;

    window.onscroll = function () {
        if (document.body.scrollTop >= 200 || document.documentElement.scrollTop >= 200) {
            navbar.classList.add('color-nav');
            if (!scrolled) {
                navbar.style.transform = 'translateY(-70px)'
            }
            setTimeout(function () {
                navbar.style.transform = 'translateY(0px)'
                scrolled = true

            }, 200)
        } else {
            navbar.classList.remove('color-nav');
            scrolled = false
        }
    };

    return (
        <div id='navbar' >
            <Navbar fixed="top" className='navbar' collapseOnSelect expand="lg"  >
                <Container>
                    <LinkContainer to='/'>
                        <Navbar.Brand className='logo' >Logo</Navbar.Brand>
                    </LinkContainer>
                    <Navbar.Toggle aria-controls="responsive-navbar-nav" />
                    <Navbar.Collapse id="responsive-navbar-nav">
                        <Nav className="me-auto">
                            <LinkContainer to='/services'>
                                <Nav.Link className='links'>Services</Nav.Link>
                            </LinkContainer>
                        </Nav>
                        <Nav>
                            <LinkContainer to='/login'>
                                <Nav.Link className='links'>Login</Nav.Link>
                            </LinkContainer>
                            <LinkContainer to='/signup'>
                                <Nav.Link className='links'>
                                    Sign Up
                                </Nav.Link>
                            </LinkContainer>
                        </Nav>
                    </Navbar.Collapse>
                </Container>
            </Navbar>
        </div >
    )
}

export default Header

enter image description here

CodePudding user response:

You are trying to get the navbar before it's rendered. That's why your variable is null. Try to use Ref to access a node element in React. The answers here can be helpful to you.

CodePudding user response:

You are going about things in what I'd like to call, "against the React way". To access the navbar element, you should be utilizing the useRef hook. This will give you an easy reference to that element through the components lifecycle.

On top of that, there are some other issues that need to be taken care of:

  • The scrolled variable not being used with useState
  • Your scroll listener not being set in a useEffect. Right now, your code would set a new listener on every re-render of your component. You want to just set it once.
  • Your scroll listener not being cleaned up.

I've made some changes to the code that I would hope will fix this issue for you.

import React, { useState, useRef, useEffect } from "react";
import { Navbar, Container, Nav } from "react-bootstrap";

import { LinkContainer } from "react-router-bootstrap";

const Header = () => {
  const [scrolled, setScrolled] = useState(false);
  const navRef = useRef();

  useEffect(() => {
    const handleScroll = () => {
      if (
        document.body.scrollTop >= 200 ||
        document.documentElement.scrollTop >= 200
      ) {
        navRef.current.classList.add("color-nav");

        if (!scrolled) {
          navRef.current.style.transform = "translateY(-70px)";
        }

        setTimeout(function () {
          navRef.current.style.transform = "translateY(0px)";
          setScrolled(true);
        }, 200);
      } else {
        navRef.current.classList.remove("color-nav");
        setScrolled(false);
      }
    };

    window.addEventListener("scroll", handleScroll);

    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, []);

  return (
    <div id="navbar" ref={navRef}>
      <Navbar fixed="top" className="navbar" collapseOnSelect expand="lg">
        <Container>
          <LinkContainer to="/">
            <Navbar.Brand className="logo">Logo</Navbar.Brand>
          </LinkContainer>
          <Navbar.Toggle aria-controls="responsive-navbar-nav" />
          <Navbar.Collapse id="responsive-navbar-nav">
            <Nav className="me-auto">
              <LinkContainer to="/services">
                <Nav.Link className="links">Services</Nav.Link>
              </LinkContainer>
            </Nav>
            <Nav>
              <LinkContainer to="/login">
                <Nav.Link className="links">Login</Nav.Link>
              </LinkContainer>
              <LinkContainer to="/signup">
                <Nav.Link className="links">Sign Up</Nav.Link>
              </LinkContainer>
            </Nav>
          </Navbar.Collapse>
        </Container>
      </Navbar>
    </div>
  );
};

export default Header;

CodePudding user response:

You are not doing it in react ways. there are many way's you could do this one of them being that is you can use useState hook keep the track of scrolled and update it on height change by this way in jsx you can just use ternary oprator to change the className like

<div className = {scrolled ? "color-nav" : "something" }
  • Related