React state and simplified logic.
I updated the navbar.
"use client";
import React from "react";
import { AiOutlineMenu, AiOutlineClose } from "react-icons/ai";
interface NavBarProps {
isSidebarOpen: boolean; // Track sidebar state
toggleSidebar: () => void; // Function to toggle sidebar
}
const NavBar: React.FC<NavBarProps> = ({ isSidebarOpen, toggleSidebar }) => {
const toggleDropdown = () => {
setDropdownVisible(!dropdownVisible);
};
return (
<nav className="bg-white backdrop-blur-md z-50 fixed top-0 left-0 right-0 mx-auto rounded-lg shadow-lg w-full p-4 flex justify-between items-center">
<div className="flex items-center space-x-4">
<div className="md:hidden">
<button onClick={toggleSidebar}>
{isSidebarOpen ? (
<AiOutlineClose className="text-3xl text-cyan-700" />
) : (
<AiOutlineMenu className="text-3xl text-cyan-700" />
)}
</button>
</div>
</div>
export default NavBar;
I updated the sidebar and passed the isOpen state as a prop.
"use client";
import { FC } from "react";
interface SidebarProps {
isOpen: boolean;
}
const Sidebar: FC<SidebarProps> = ({ isOpen }) => {
return (
<aside
className={`${
styles.sidebar
} fixed top-[64px] left-0 h-[calc(100vh-64px)] w-64 bg-[#f0f0f0] text-black text-md transform ${
isOpen ? "translate-x-0" : "-translate-x-full"
} md:translate-x-0 transition-transform duration-300 ease-in-out z-40 overflow-y-auto`}
>
{/* Profile Section */}
{/* Main Navigation */}
</aside>
);
};
export default Sidebar;
Lastly, I updated the layout component.
I passed the
isSidebarOpen
state and thetoggleSidebar
function from theLayout
component to bothNavBar
andSidebar
.
"use client";
import { useState } from "react";
import Sidebar from "./Sidebar";
import NavBar from "./dashcomponents/Navbar";
export default function Layout({ children }: { children: React.ReactNode }) {
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
const toggleSidebar = () => {
setIsSidebarOpen((prev) => !prev);
};
return (
<div className="h-screen">
{/* Sidebar */}
<Sidebar isOpen={isSidebarOpen} />
<div className="flex flex-col">
{/* NavBar */}
<NavBar isSidebarOpen={isSidebarOpen} toggleSidebar={toggleSidebar} />
{/* Main Content */}
<main className="my-[6rem] md:ml-[18rem] w-[90%] mx-auto md:max-w-5xl overflow-x-hidden overflow-y-auto">
{children}
</main>
</div>
</div>
);
}
Logic Explanation:
NavBar: The hamburger menu (or close icon) is conditionally rendered using a ternary operator based on the state of
isSidebarOpen
. IfisSidebarOpen
istrue
, it shows the close icon (AiOutlineClose
); otherwise, it shows the hamburger menu (AiOutlineMenu
).When the button is clicked, it triggers the
toggleSidebar
function, which toggles the value ofisSidebarOpen
betweentrue
andfalse
. This causes a re-render in theNavBar
to switch between the hamburger and close icons, and also controls the visibility of the sidebar.Sidebar: The sidebar is controlled by the
isOpen
prop, which determines whether it is shown or hidden. The sidebar slides in and out by applying CSS classes using the Tailwind utility classes liketranslate-x-0
(to show) and-translate-x-full
(to hide). These classes are toggled based on theisOpen
prop.Layout Component: This component manages the state of the sidebar (
isSidebarOpen
) and passes it along with thetoggleSidebar
function as props to both theNavBar
and theSidebar
. This allows the sidebar to be opened and closed when the user clicks the button in theNavBar
.
Expected Behavior:
On mobile devices or smaller screens, the
NavBar
will show the hamburger menu when the sidebar is hidden and the close icon when the sidebar is open.When the user clicks the hamburger menu, the sidebar will slide into view, and the menu icon will change to the close icon.
Clicking the close icon will slide the sidebar out of view and change the icon back to the hamburger menu.
The function toggleSidebar
is used to toggle the visibility of the sidebar by changing the isSidebarOpen
state.
Here's a detailed breakdown of what is happening:
const toggleSidebar = () => {
setIsSidebarOpen((prev) => !prev);
};
Key Concepts:
State Update Function (
setIsSidebarOpen
):setIsSidebarOpen
is a state update function provided by theuseState
hook.The purpose of this function is to update the value of the
isSidebarOpen
state, which determines whether the sidebar is open or closed.
Previous State (
prev
):In this case, the
setIsSidebarOpen
function is using a callback form. When you pass a function into the state update function, React provides the previous state value (prev
) as the argument to that function.This means that instead of directly setting the state to
true
orfalse
, you're getting the current state value (prev
) and then determining what the next value should be.
Toggle Logic (
!prev
):The
!prev
expression is a logical NOT operator, which flips the value ofprev
.If
prev
(the previous state) istrue
,!prev
will befalse
. Ifprev
isfalse
,!prev
will betrue
.In other words, it inverts the current state. If the sidebar is currently open (i.e.,
isSidebarOpen
istrue
), it will set it tofalse
(closing the sidebar), and if the sidebar is closed (isSidebarOpen
isfalse
), it will set it totrue
(opening the sidebar).
What Happens in the Application:
When
toggleSidebar
is called (e.g., when the user clicks the hamburger menu or close icon), it checks the current state of the sidebar (isSidebarOpen
).If the sidebar is open (
isSidebarOpen === true
), callingtoggleSidebar
will close it (setisSidebarOpen
tofalse
).If the sidebar is closed (
isSidebarOpen === false
), callingtoggleSidebar
will open it (setisSidebarOpen
totrue
).
This allows the sidebar to be toggled on and off whenever the function is invoked.
Why Use the Callback Form?
Using the callback form of setIsSidebarOpen((prev) => !prev)
ensures that you're always working with the most up-to-date state value. This is particularly important if multiple components or functions might change the state asynchronously. In contrast, using setIsSidebarOpen(!isSidebarOpen)
directly could cause issues if React batches multiple state updates.
Example:
Let's say the isSidebarOpen
state is currently false
(sidebar is closed).
When you click the menu button,
toggleSidebar
is called.Inside
toggleSidebar
, theprev
value will befalse
, because that was the previous state.The
!prev
expression flipsfalse
totrue
, soisSidebarOpen
is updated totrue
(sidebar is now open).The component re-renders, showing the sidebar.
If you click the button again, the process repeats but this time the previous state (prev
) will be true
, and the sidebar will be closed.
This approach makes the function concise and ensures the correct toggling behavior for the sidebar.
Object destructuring ({ isOpen })
In React, ({ isOpen })
is a form of object destructuring used to access the isOpen
property from an object directly.
What does ({ isOpen })
mean?
When you see ({ isOpen })
in a function parameter, it means that an object is being passed to the function, and you want to extract the isOpen
property from that object right away without needing to access it using the dot notation like props.isOpen
. Essentially, it's a shortcut.
For example:
const MyComponent = ({ isOpen }) => {
return <div>{isOpen ? 'Sidebar is open' : 'Sidebar is closed'}</div>;
};
Breakdown:
isOpen
is a boolean value (or any value) that could be passed as a prop.Instead of doing this:
const MyComponent = (props) => { return <div>{props.isOpen ? 'Sidebar is open' : 'Sidebar is closed'}</div>; };
You can use destructuring directly in the function argument to access
isOpen
:const MyComponent = ({ isOpen }) => { return <div>{isOpen ? 'Sidebar is open' : 'Sidebar is closed'}</div>; };
Example Usage:
In the context of your sidebar, isOpen
might be a prop passed down to a child component or a part of your component’s state that controls the visibility of the sidebar.
const Sidebar = ({ isOpen }) => {
return (
<aside className={isOpen ? "sidebar-open" : "sidebar-closed"}>
{/* Sidebar content */}
</aside>
);
};
Explanation:
Object Destructuring: Instead of passing and then accessing the whole
props
object (e.g.,props.isOpen
), you are only grabbing theisOpen
property directly. It makes the code more concise.isOpen
as a Prop: IfisOpen
is passed to theSidebar
component, this means the parent component is controlling whether the sidebar is open or closed, and the child component is rendering based on this value.
When Is This Used?
This is commonly used in functional components when you need to extract specific properties (props or state) passed into the component.
It makes the code cleaner and easier to read, especially when dealing with multiple properties, like this:
const MyComponent = ({ isOpen, toggleSidebar, title }) => { return ( <div> <h1>{title}</h1> <button onClick={toggleSidebar}>{isOpen ? 'Close Sidebar' : 'Open Sidebar'}</button> </div> ); };
In this case, isOpen
, toggleSidebar
, and title
are destructured right in the function signature, so you can directly access them without needing to write props.isOpen
, props.toggleSidebar
, or props.title
each time.
Subscribe to my newsletter
Read articles from Hillary Nyakundi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Hillary Nyakundi
Hillary Nyakundi
I am a software engineer who is fascinated with building exclusive technology.