Search code examples
cssreactjstogglesidebarresponsiveness

How to achieve toggling (from visible to hidden and vice versa) the sidebar in React


I have a state in React

// sidebar toggled
const [sidebarToggled, setSidebarToggled] = useState(false);

sidebarToggled(false) = sidebar is visible

sidebarToggled(true) = sidebar is hidden

I have a button that toggles the sidebar

<button onClick={()=>setSidebarToggled(!sidebarToggled)}>Toggle</button>

Expected Behaviour

When I am on a desktop screen (larger than 768px), sidebarToggled is true, which means the sidebar is visible, and when I am using Chrome console to check responsiveness or by minimizing and maximizing the screen, when the screen is less than 768px sidebarToggled is set to true to make the sidebar hidden and when it is larger than 768px sidebarToggled is set to false to make it visible again. It is working and I achieved this by following code

// toggle sidebar when screen reaches 768px
  useEffect(() => {
    const handleResize = () => {
      if (window.innerWidth <= 768) {
        setSidebarToggled(true);
      }
      else{
        setSidebarToggled(false);
      }
    };
    // Add event listener for window resize
    window.addEventListener('resize', handleResize);

    // Cleanup the event listener on component unmount
    return () => window.removeEventListener('resize', handleResize);
  }, []);

Now comes the main point

If I set the sidebarToggled to true by clicking the button from a device larger than 768px then no matter what screen size we have sidebarToggled should always remain true and the sidebar should always remain hidden. Similarly if from a mobile device I set the sidebarToggled to false then minimizing or maximizing should have no effect on sidebarToggled, it should always remain false and visible.

http://webapplayers.com/homer_admin-v2.0/index.html

This behavior from the above site is what I want in my React App.


Solution

  • Conceptually you have two pieces of state.

    1. The explicitly set preference set by the user clicking a button
    2. The screen size being below or above a threshold

    You cannot and need not represent both as one boolean. Split the state in two and derive the UI state from the two state variables. Here I've extracted the logic into a hook.

    const useSidebarState = (widthThreshold = 768) => {
      const [sidebarExplicitlyHidden, setSidebarExplicitlyHidden] = useState(null);
      const [screenWidthAboveThreshold, setScreenWidthAboveThreshold] = useState(
        window.innerWidth > widthThreshold
      );
      useEffect(
        () => {
          const handleResize = () => void setScreenWidthAboveThreshold(
            window.innerWidth > widthThreshold
          );
          handleResize();
          window.addEventListener('resize', handleResize);
          return () => window.removeEventListener('resize', handleResize);
        },
        [widthThreshold]
      );
      const toggleSidebar = useCallback(
        () => void setSidebarExplicitlyHidden(prevValue => !prevValue)
      );
      return {
        sidebarToggled: sidebarExplicitlyHidden ?? !screenWidthAboveThreshold,
        toggleSidebar
      };
    };
    

    Pass toggleSidebar as an click handler to your toggle button.