Search code examples
javascriptreactjsaddeventlistener

Calling a function on event listener in react but shows error `Cannot read properties of undefined (reading 'classList')` in react


I added a event listener to the document for a keydown event, and the event listener works, within the event listener I call a function, and even that function works perfectly, everything works, but I still get a:

Cannot read properties of undefined (reading 'classList')
TypeError: Cannot read properties of undefined (reading 'classList')
    at toggleSearch (http://localhost:3000/static/js/bundle.js:236:29)
    at HTMLDocument.<anonymous> (http://localhost:3000/static/js/bundle.js:293:7)

error, and if I close the error from the top left cross(x) icon, I can see that the function executed perfectly, but why I am getting the error if it works?

code:

import Logo from "./Logo";
import React, { useRef, useEffect, useState } from "react";
import useEventListener from "../hooks/useEventListerner";
import axios from "axios";
// import getResults from "../hooks/search";

function Header() {
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
  const menuButton = useRef();
  const menu = useRef();
  const searchButton = useRef();
  const searchButton2 = useRef();
  const searchContainer = useRef();
  const cancelButton = useRef();
  const searchInput = useRef();
  const result = useRef();
  const url = "https://graphql.anilist.co";
  const searchString = `query ($query: String){ Page(page: 1, perPage: 10) { media(search: $query, type: MANGA, sort: SEARCH_MATCH) { id coverImage { medium } title { english romaji } averageScore startDate { year } isAdult status }}}`;
  const advance = useRef();
  // Scrollbar.initAll();
  console.log(result, typeof result, result.current, typeof result.current);
  function SearchCard(props) {
    if (props.isAdult === true) {
      var adultCont = "nsfw-cont";
      var adultImage = "nsfw";
    } else {
      // eslint-disable-next-line no-redeclare
      var adultCont = "";
      // eslint-disable-next-line no-redeclare
      var adultImage = "";
    }

    return (
      <a href={`/manga/${props.id}`} className="search-card">
        <div className={`search cover-container ${adultCont}`}>
          <img
            src={props.coverLink}
            alt=""
            className={`search cover ${adultImage}`}
            loading="lazy"
          />
        </div>
        <div className="search info">
          <h3 className="search title nsfw-title">
            <p>{props.title}</p>
          </h3>
          <span className="search subinfo">
            <p>★ {props.ratings}</p>
            <p className="bullet">•</p>
            <p>{props.release.year}</p>
            <p className="bullet">•</p>
            <p>{props.status}</p>
          </span>
        </div>
      </a>
    );
  }

  function toggleMenu() {
    menu.current.classList.toggle("visible-flex");
  }
  function toggleSearch() {
    searchContainer.current.classList.toggle("visible-flex");
    searchInput.current.focus();
  }

  var [advanceElem, setAdvanceElem] = useState(
    <p className="result-text">Search results will appear here</p>
  );

  function inputSearch() {
    const query = searchInput.current.value;
    if (query === "") {
      setAdvanceElem(
        <p className="result-text">Search results will appear here</p>
      );
    } else {
      setAdvanceElem(
        <a href={`/search/manga/${query}`} className="search-card advance">
          Advance Search for <b className="query">{query}</b>
        </a>
      );
    }
    axios
      .post(
        url,
        {
          query: searchString,
          variables: { query: query },
        },
        {
          headers: {
            "Content-Type": "application/json",
            Accept: "application/json",
          },
        }
      )
      .then((response) => {
        console.log(response);
      });
  }

  document.addEventListener("keydown", function (event) {
    if (event.ctrlKey && event.key === "k") {
      event.preventDefault();
      toggleSearch();
    }
  });

  useEventListener("keyup", inputSearch, searchInput);
  useEventListener("click", toggleSearch, searchButton);
  useEventListener("click", toggleSearch, searchButton2);
  useEventListener("click", toggleMenu, menuButton);
  useEventListener("click", toggleSearch, cancelButton);
  return (
    <header>
      <div className="header-inner wrapper">
        <Logo />
        <nav className="navbar">
          <button className="search-button" ref={searchButton}>
            <span className="search-icon nobdr">
              <svg
                className="fill-icon search-ic"
                viewBox="0 0 20 20"
                xmlns="http://www.w3.org/2000/svg"
                fill="none"
              >
                <g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
                <g
                  id="SVGRepo_tracerCarrier"
                  strokeLinecap="round"
                  stroklinejoin="round"
                ></g>
                <g id="SVGRepo_iconCarrier">
                  <path
                    fillRule="evenodd"
                    d="M4 9a5 5 0 1110 0A5 5 0 014 9zm5-7a7 7 0 104.2 12.6.999.999 0 00.093.107l3 3a1 1 0 001.414-1.414l-3-3a.999.999 0 00-.107-.093A7 7 0 009 2z"
                  ></path>
                </g>
              </svg>
            </span>
            <p className="placeholder">Search</p>
            <div className="keys">
              <kbd className="key">CTRL</kbd>
              <kbd className="key">K</kbd>
            </div>
          </button>
          <button
            className="nav-link wide"
            id="search-button"
            ref={searchButton2}
          >
            <span className="search-icon nobdr">
              <svg
                className="fill-icon search-ic"
                viewBox="0 0 20 20"
                xmlns="http://www.w3.org/2000/svg"
                fill="none"
              >
                <g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
                <g
                  id="SVGRepo_tracerCarrier"
                  strokeLinecap="round"
                  stroklinejoin="round"
                ></g>
                <g id="SVGRepo_iconCarrier">
                  <path
                    fillRule="evenodd"
                    d="M4 9a5 5 0 1110 0A5 5 0 014 9zm5-7a7 7 0 104.2 12.6.999.999 0 00.093.107l3 3a1 1 0 001.414-1.414l-3-3a.999.999 0 00-.107-.093A7 7 0 009 2z"
                  ></path>
                </g>
              </svg>
            </span>
          </button>
          <a href="/" className="nav-link wide">
            Library
          </a>
          <a href="/" className="nav-link smol">
            <svg
              className="fill-icon search-ic"
              viewBox="-8 0 60 60"
              xmlns="http://www.w3.org/2000/svg"
              fill="#000000"
            >
              <g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
              <g
                id="SVGRepo_tracerCarrier"
                stroke-linecap="round"
                srokelinejoin="round"
              ></g>
              <g id="SVGRepo_iconCarrier">
                <path
                  className="cls-1"
                  d="M222,150a4,4,0,0,1-4-4V94a4,4,0,0,1,4-4h36l4,4H224a2,2,0,0,0-2,2v4a2,2,0,0,0,2,2,2,2,0,0,1,2,2v11.086a1,1,0,0,0,1.707.707l2.879-2.879a2,2,0,0,1,2.828,0l2.879,2.879a1,1,0,0,0,1.707-.707V104a2,2,0,0,1,2-2h22v48H222Z"
                  id="book"
                  transform="translate(-218 -90)"
                ></path>
              </g>
            </svg>
          </a>
          <button
            className="nav-link link-icon"
            id="menuButton"
            ref={menuButton}
          >
            <span className="dot a"></span>
            <span className="dot b"></span>
            <span className="dot c"></span>
          </button>
        </nav>
        <div className="menu" id="menu" ref={menu}>
          <a href="/" className="nav-link menu-link">
            Profile
          </a>
          <a href="/" className="nav-link menu-link">
            Discord
          </a>
          <a href="/" className="nav-link menu-link">
            Settings
          </a>
        </div>
        <div className="search-wrapper" ref={searchContainer}>
          <div className="search-container" id="search-container">
            <div className="searchbar">
              <span className="search-icon" id="search-icon">
                <svg
                  className="fill-icon search-ic"
                  viewBox="0 0 20 20"
                  xmlns="http://www.w3.org/2000/svg"
                  fill="none"
                >
                  <g id="SVGRepo_bgCarrier" strokeWidth="0"></g>
                  <g
                    id="SVGRepo_tracerCarrier"
                    strokeLinecap="round"
                    stroklinejoin="round"
                  ></g>
                  <g id="SVGRepo_iconCarrier">
                    <path
                      fillRule="evenodd"
                      d="M4 9a5 5 0 1110 0A5 5 0 014 9zm5-7a7 7 0 104.2 12.6.999.999 0 00.093.107l3 3a1 1 0 001.414-1.414l-3-3a.999.999 0 00-.107-.093A7 7 0 009 2z"
                    ></path>
                  </g>
                </svg>
              </span>
              <input
                type="text"
                id="search-input"
                placeholder="Search"
                ref={searchInput}
                autoComplete="off"
              />
              <button className="cancel" id="cancel" ref={cancelButton}>
                Cancel
              </button>
            </div>
            <div className="search-results" ref={result}>
              {advanceElem}
            </div>
          </div>
        </div>
      </div>
    </header>
  );
}

export default Header;
//useEventListener.js

import { useEffect } from "react";

function useEventListener(eventName, handler, element = window) {
  useEffect(() => {
    element.current.addEventListener(eventName, handler);

    return () => {
      element.current.removeEventListener(eventName, handler);
    };
  }, [eventName, handler, element]);
}

export default useEventListener;

Solution

  •  const keyHandler = useCallback(
        (event) => {
          if (event.key.toLowerCase() === "k" && event.ctrlKey) {
            event.preventDefault();
            if (document.activeElement === searchInput.current) {
              searchInput.current.blur();
            } else {
              searchInput.current.focus();
            }
          } else if (event.key === "Escape") {
            searchInput.current.blur();
          }
        },
        [searchInput]
      );
    
      useEffect(() => {
        window.addEventListener("keydown", keyHandler);
        return () => window.removeEventListener("keydown", keyHandler);
      }, [searchInput, keyHandler]);
    

    It worked when I made it like this.