How to Create a Dropdown in React with Tailwind CSS Using useReducer
and useRef
Creating dropdown menus in a React application can enhance user experience by providing a compact way to navigate and access additional information. In this guide, we will implement two dropdowns—one for notifications and one for user profile settings—using useReducer
for state management and useRef
to handle outside clicks to close the dropdowns. We will also incorporate Font Awesome icons for a polished look.
Introduction
In modern web development, managing user interfaces efficiently is crucial. React, combined with Tailwind CSS, provides a powerful toolkit for building responsive components. We will learn how to handle dropdown functionality in React, ensuring that clicking outside of a dropdown will close it, while maintaining the ability to open or close each dropdown independently.
What Are useReducer
and useRef
?
Before diving into the code, let’s understand the two React hooks we’ll be using:
useReducer
: This hook is an alternative touseState
for managing state in functional components. It is especially useful for managing complex state logic and multiple state variables. TheuseReducer
hook takes a reducer function and an initial state, returning the current state and a dispatch function to update that state.useRef
: This hook provides a way to reference DOM elements directly. It is useful for accessing and manipulating elements without triggering re-renders. In our case, we will useuseRef
to check if a click happened outside the dropdown menus.
Step-by-Step Implementation
Step 1: Set Up the Project
First, ensure you have a React project set up with Tailwind CSS. If you don't have one, you can create it using Create React App:
npx create-react-app my-dropdown-app
cd my-dropdown-app
npm install tailwindcss
npx tailwindcss init
Configure Tailwind by adding the following lines to your tailwind.config.js
:
module.exports = {
purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
darkMode: false,
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
};
Then, add the Tailwind directives to your index.css
:
@tailwind base;
@tailwind components;
@tailwind utilities;
Step 2: Install Font Awesome
To use Font Awesome icons, you need to install the @fortawesome/react-fontawesome
package:
npm install @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons
Step 3: Create the Navbar Component
In your src
directory, create a new file named Navbar.tsx
. This component will contain the dropdowns.
Implement the Navbar Code
Here’s the code for the Navbar
component, which utilizes useReducer
and useRef
to handle dropdown states and outside clicks.
import React, { useRef, useEffect, useReducer } from "react";
import { Link } from "react-router-dom";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faBell, faUser, faCaretDown } from '@fortawesome/free-solid-svg-icons';
interface IState {
isProfileOpen: boolean;
isNotificationOpen: boolean;
}
type Action =
| { type: 'toggleProfile' }
| { type: 'toggleNotification' }
| { type: 'closeAll' };
function reducer(state: IState, action: Action): IState {
switch (action.type) {
case 'toggleProfile':
return {
isProfileOpen: !state.isProfileOpen,
isNotificationOpen: false
};
case 'toggleNotification':
return {
isProfileOpen: false,
isNotificationOpen: !state.isNotificationOpen
};
case 'closeAll':
return {
isProfileOpen: false,
isNotificationOpen: false
};
default:
return state;
}
}
const Navbar: React.FC = () => {
const [state, dispatch] = useReducer(reducer, { isProfileOpen: false, isNotificationOpen: false });
const profileDropdownRef = useRef<HTMLDivElement>(null);
const notificationDropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
const target = event.target as Node;
if (
(profileDropdownRef.current && !profileDropdownRef.current.contains(target)) ||
(notificationDropdownRef.current && !notificationDropdownRef.current.contains(target))
) {
dispatch({ type: 'closeAll' });
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => {
document.removeEventListener("mousedown", handleClickOutside);
};
}, []);
const toggleProfileDropdown = (event: React.MouseEvent) => {
event.stopPropagation();
dispatch({ type: 'toggleProfile' });
};
const toggleNotificationDropdown = (event: React.MouseEvent) => {
event.stopPropagation();
dispatch({ type: 'toggleNotification' });
};
const closeDropdowns = () => {
dispatch({ type: 'closeAll' });
};
const notificationItems = [
{ text: "New data received", time: "2 Days Ago" },
{ text: "New update available", time: "1 Day Ago" },
{ text: "Scheduled maintenance", time: "3 Days Ago" },
];
const profileItems = [
{ label: "Profile", link: "#" },
{ label: "Settings", link: "#" },
{ label: "Logout", link: "#" }
];
return (
<header className="w-full bg-white shadow p-4 sticky">
<nav className="flex justify-between items-center">
<div className="text-2xl font-bold">DC Dashboard</div>
<div className="flex items-center space-x-6">
<input
type="text"
placeholder="Search"
className="hidden md:block px-3 py-1 border border-gray-300 rounded-md focus:outline-none focus:ring focus:border-black-300 transition-all duration-300"
/>
<div className="relative flex items-center">
<button onClick={toggleNotificationDropdown} className="h-8 w-6 text-gray-500">
<FontAwesomeIcon icon={faBell} />
<span className="absolute top-0 right-0 bg-black text-white text-xs rounded-full w-4 h-4 flex items-center justify-center">2</span>
</button>
<div
ref={notificationDropdownRef}
className={`absolute top-8 right-0 mt-4 z-20 w-96 bg-white rounded-xl shadow flex-col ${state.isNotificationOpen ? "" : "hidden"}`}
>
<h6 className="self-stretch px-5 py-2.5 text-black font-semibold">Notifications</h6>
<ul className="pb-2 w-full">
{notificationItems.map((item, index) => (
<li key={index} className="px-5 py-2.5 border-t border-black/10 hover:bg-gray-100 cursor-pointer" onClick={closeDropdowns}>
<div className="flex justify-between">
<p className="text-xs font-bold">{item.text}</p>
<p className="text-xs font-normal">{item.time}</p>
</div>
</li>
))}
</ul>
</div>
</div>
<div className="relative">
<button className="flex items-center space-x-2" onClick={toggleProfileDropdown}>
<FontAwesomeIcon icon={faUser} />
<span className="hidden md:block font-medium">CodeWithChintan</span>
<FontAwesomeIcon icon={faCaretDown} />
</button>
<div
ref={profileDropdownRef}
className={`absolute right-0 mt-4 w-48 bg-white shadow-md rounded-md z-10 ${state.isProfileOpen ? "" : "hidden"}`}
>
<ul className="py-2">
{profileItems.map((item, index) => (
<li key={index} className="px-4 py-2 hover:bg-gray-100 cursor-pointer" onClick={closeDropdowns}>
<Link to={item.link}>{item.label}</Link>
</li>
))}
</ul>
</div>
</div>
</div>
</nav>
</header>
);
};
export default Navbar;
Step 4: Integrate the Navbar in Your App
Open your App.tsx
file and import the Navbar
component to include it in your application layout.
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Navbar from './components/Navbar';
const App: React.FC = () => {
return (
<Router>
<Navbar />
<main className="p-4">
<h1 className="text-2xl font-bold">Welcome to DC Dashboard!</h1>
{/* Other components and content */}
</main>
</Router>
);
};
export default App;
Step 5: Style with Tailwind CSS
The provided classes from Tailwind CSS should already give a neat design. However, feel free to customize styles as needed.
Step 6: Test the Application
Start your application by running:
bash
npm start
You should now see a navigation bar at the top of your application with functional dropdowns for notifications and user profile settings.
FAQ
1. How does the useReducer
hook work in this example?
The useReducer
hook allows us to manage the state of multiple dropdowns efficiently. We define a reducer function that takes the current state and an action to return the new state. This pattern is helpful for toggling dropdowns and handling the logic for closing all dropdowns at once.
2. Why use useRef
?
We use useRef
to reference the dropdown elements. This lets us check if a click event occurred outside these elements. If it does, we dispatch an action to close the dropdowns, ensuring a clean user experience.
3. Can I add more dropdowns?
Yes! You can extend the state in the reducer and add more dropdowns similarly. Just ensure each dropdown has its own ref and toggle function.
4. Is Tailwind CSS necessary for this implementation?
No, Tailwind CSS is not mandatory. You can style your dropdowns with any CSS framework or custom CSS styles, but Tailwind makes styling quicker and more responsive.
Conclusion
In this guide, you’ve learned how to create a functional dropdown menu in React using useReducer
for state management and useRef
for handling outside clicks. This approach provides a clean and efficient way to manage complex UI interactions, enhancing the overall user experience. Feel free to build upon this foundation and customize it to fit your application's needs!
Top comments (0)