Search code examples
reactjsreact-hookscss-transitionsfadein

fadeout sidebar when user doesnt use it for more than 5sec


Expected Scenario: if the user doesn't use the sidebar for more than 5 seconds, the sidebar fadeout (opacity set to 20%) and fade in (opacity:100%) when mouse is 20px nearby.

Current implementation what i got is: when I load the page and on sidebar if its not hovered for 5 sec, its faded out and when i hover on it, it fades in (opacity 100%) and when i hover out (mouseleave) it is 100%.

I am failing to achieve when mouse is 20px nearby - fade in.

  const [isSidebarFaded, setSidebarFaded] = useState(false)
  const [isHovered, setHovered] = useState(false)

  
useEffect(() => {
    if (!isHovered) {
      const timerId = setTimeout(() => {
        //after 5 seconds if not hovered
        setSidebarFaded(true)
      }, 5000)
      return () => {
        clearTimeout(timerId)
      }
    }
  }, [])
    
  const handleMouseEnter = () => {
    setHovered(true)
    if (isSidebarFaded) {
      setSidebarFaded(false)
    }
  }

  const handleMouseLeave = () => {
    setHovered(false)
    if (isSidebarFaded)
      setSidebarFaded(true)
  }

return(
      <div
        className={classNames(styles.component, {
          [styles.fadeOut]: isSidebarFaded,
          })}
        onMouseEnter={handleMouseEnter}
        onMouseLeave={handleMouseLeave}
      >
)

Solution

  • Create a wrapper around the actual sidebar, with a padding of 20px. Put the event handlers on the wrapper.

    In my version of your code, there is a single timeout with ref, that the event handlers start/stop:

    const { useState, useRef, useCallback, useEffect } = React
    
    const Demo = () => {
      const [isSidebarFaded, setSidebarFaded] = useState(false)
      const timeout = useRef()
        
      const handleMouseEnter = useCallback(() => {
        setSidebarFaded(false)
        clearTimeout(timeout.current)
      }, [])
    
      const handleMouseLeave = useCallback(() => {
        clearTimeout(timeout.current)
        
        timeout.current = setTimeout(() => {
          setSidebarFaded(true)
        }, 2000)
      }, [])
      
      useEffect(() => {
        handleMouseLeave();
        
        return () => {
          clearTimeout(timeout.current)
        }
      }, [handleMouseLeave])
    
      return(
        <div
          className={classNames({ sidebarContainer: true, sidebarFaded: isSidebarFaded })}
          onMouseEnter={handleMouseEnter}
          onMouseLeave={handleMouseLeave}
        >
          <div className="sidebar"></div>
        </div>
      )
    }
    
    ReactDOM
      .createRoot(root)
      .render(<Demo />)
    .sidebarContainer {
      position: absolute;
      top: 0;
      bottom: 0;
      left: 0;
      width: 100px;
      padding-right: 20px;
      opacity: 1;
      transition: opacity 0.3s;
    }
    
    .sidebarFaded {
      opacity: 0.2;
    }
    
    .sidebar {
      height: 100%;
      width: 100%;
      background: blue;
    }
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.3.2/index.min.js" integrity="sha512-GqhSAi+WYQlHmNWiE4TQsVa7HVKctQMdgUMA+1RogjxOPdv9Kj59/no5BEvJgpvuMTYw2JRQu/szumfVXdowag==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    
    <div id="root"></div>