Search code examples
reactjsresponsive-designreact-hookssidebar

Responsive sidebar using react hooks


I'm trying to debug an issue that I'm having with my sidebar but can't figure out what I'm doing wrong. The sidebar is correctly collapsing based on the innerWidth but when I am on mobile view on the first load the sidebar is expanded rather than collapse as suppose to be. Any help that explains to me what is wrong would be great. Thanks a lot Here is my snippet:

export default function Sidebar() {
  const location = useLocation();
  let { pathname } = location;

  const [isNavOpen, setIsNavOpen] = useState(true);

  useEffect(() => {
    window.addEventListener("resize", () => {
      if (window.innerWidth <= 767) {
        setIsNavOpen(false);
      }
      else if (window.innerWidth >= 767) {
        setIsNavOpen(true);
      }
    });
  });

  return (
    <div className="menu-bar">
      <Menu
        width={210}
        isOpen={isNavOpen}
        noOverlay
        pageWrapId={"page-wrap"}
        outerContainerId={"outer-container"}
        disableAutoFocus
        disableCloseOnEsc
      >

Solution

  • Your initial state is true, so when the app starts, the sidebar is open. As long as you don't resize, the event handler is not called.

    Extract the logic that defines the state of isNavOpen to a function, and call it to create the initial value, and then when the window is resized:

    const { useState, useEffect } = React;
    
    const shouldBeOpen = () => window.innerWidth > 767
    
    function Sidebar() {
      const [isNavOpen, setIsNavOpen] = useState(shouldBeOpen);
    
      useEffect(() => {
        window.addEventListener("resize", () => {
          setIsNavOpen(shouldBeOpen());
        });
      }, []);
    
      return isNavOpen ? 'open' : 'close'
    }
    
    ReactDOM.render(
      <Sidebar />,
      root
    );
    <script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
    
    <div id="root"></div>

    In addition, in this case I prefer the use of window.matchMedia(), which is the JS equivalent to CSS media queries. I've created a useMatchMedia hook which you can see in this answer.