Home > Net >  useEffect triggered after onClick event - with different results
useEffect triggered after onClick event - with different results

Time:04-09

I wanted to create a dropdown menu, which shows itself and hides on hovering, and disappears after clicking its item. I thought I found a way to do it - but it works only sometimes. (Or maybe it doesn't work - but sometimes it does.) Details below:

  1. I gotta DropdownMenu2 component, which display is being toggled by onMouseEnter/Leave events. This component (my dropdown menu) holds inside <NavLink> menu items.
  2. I wanted the dropdown menu to disappear after clicking on menu item, so inside <Navlink> I created onClick event which triggers handleClick. This functions sets a click variable - to a CSS className with display:none. click is then passed to <div> that contains the Dropdown menu.
  3. To toggle the dropdown menu display again on mouse hover, I had to get rid of the click class from the div. For that I created useEffect hook, with click dependency - so it fires every time click state changes. And function inside this hook - changes click value, so it no longer represents the CSS display:none class. So after (2.) - div containing dropdown menu has display:none, disapears, and useEffect erases that - making it hover ready.

problem:

this works only sometimes - sometimes useEffect is triggered so fast after onClick, that the dropdown menu doesn't even drop. ( click changes so fast that div container gets the "erasing" class immediately after display:none class )

NaviMainButtonDrop2

import DropdownMenu2 from "./DropdownMenu2";
import useHoverButton from "./sub-components/useHoverButton";

const NaviMainButtonDrop2 = () => {

  const { disp, hoverOn, hoverOff } = useHoverButton();

  return (
    <li 
      className={`nav-main__button dropdown-us`} 
    >
        <a 
          className="hover-pointer"
          onm ouseEnter={hoverOn}
          onm ouseLeave={hoverOff}
          >
            title
          </a>
        { disp && <DropdownMenu2 /> }
    </li>
  )
}
export default NaviMainButtonDrop2

useHoverButton (custom hook for NaviMainButtonDrop2)

import { useState } from "react";

const useHoverButton = () => {

    const [disp, setDisp] = useState(false);   
    const hoverOn = () => setDisp(true)
    const hoverOff = () => setDisp(false)


  return { disp, hoverOn, hoverOff }
}
export default useHoverButton

DropdownMenu2

import "./DropdownMenu.css"
import { NavLink } from "react-router-dom";
import { MenuItemContentSchool } from "./sub-components/MenuItemContentSchool"
import { useEffect } from "react";
import useAddClass from "./sub-components/useAddClass";

const DropdownMenu2 = () => {

  const { click, setClick, handleClick } = useAddClass("hide-menu");

  useEffect(() => {
    console.log("[usEffect]")
    setClick("");
  }, [click]);

  return (
    <div className={`dropdown-holder-us ${click}`}>
      {/* here menu unfolds */}
    {MenuItemContentSchool.map((item) => {
      return (
        <NavLink
            to={item.link} 
            className={(navData) => (navData.isActive ? "d-content-us active-style" : 'd-content-us')}
            onClick={handleClick}  
            key={item.id} 
        >
          {item.title}
        </NavLink> 
      )
    })}
    </div>
  )
}
export default DropdownMenu2

useAddClass (custom hook for DropdownMenu2)

import { useState } from "react"

const useAddClass = (className) => {

    const [click, setClick] = useState("");
    const handleClick = () => setClick(className); 
    
  return { click , handleClick }
}
export default useAddClass

CodePudding user response:

I think the issue here is that you are not able to get the latest state whenever you update the next state that is why it works sometimes and sometimes it doesn't.

According to me there could be 2 solutions to this, either use a setTimeout or get the latest state when setting the state.

  1. setTimeout solution-
 useEffect(() => {

    setTimeout(() => {
      setClick("")
    },2000)
  1. Try and always get the latest state when you update the next state.
useEffect(() => {
    console.log("[usEffect]")
    setClick((clickLatest) => "");
  }, [click]);

and

const handleClick = () => setClick((clickLatest) => className); 

This callback will help the useState wait for the latest state and then update the state further. Thus this might solve your issue.

Let me know if this works. (Please accept the answer if it does)

CodePudding user response:

I think I just found a simple solution to this. I don't understand why useEffect seems to work in a random timing, but using setTimeOut inside it, and delaying the execution of setClick - seems to do the job.

 useEffect(() => {

    setTimeout(() => {
      setClick("")
    },2000)
  • Related