”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > React 中的智能下拉菜单:使用 useReducer 和 useRef 进行外部点击处理

React 中的智能下拉菜单:使用 useReducer 和 useRef 进行外部点击处理

发布于2024-11-03
浏览:761

Smart Dropdowns in React: Using useReducer and useRef for Outside Click Handling

如何使用 useReducer 和 useRef 在 React 中使用 Tailwind CSS 创建下拉菜单

在 React 应用程序中创建下拉菜单可以通过提供一种紧凑的方式来导航和访问附加信息,从而增强用户体验。在本指南中,我们将实现两个下拉菜单,一个用于通知,一个用于用户配置文件设置,使用 useReducer 进行状态管理,使用 useRef 处理外部点击以关闭下拉菜单。我们还将合并 Font Awesome 图标,以获得精美的外观。

介绍

在现代 Web 开发中,有效管理用户界面至关重要。 React 与 Tailwind CSS 相结合,提供了用于构建响应式组件的强大工具包。我们将学习如何在 React 中处理下拉菜单功能,确保单击下拉菜单外部将关闭它,同时保持独立打开或关闭每个下拉菜单的能力。

useReducer 和 useRef 是什么?

在深入代码之前,让我们先了解一下我们将使用的两个 React hook:

  • useReducer:此钩子是 useState 的替代方案,用于管理功能组件中的状态。它对于管理复杂的状态逻辑和多个状态变量特别有用。 useReducer 钩子接受一个减速器函数和一个初始状态,返回当前状态和一个调度函数来更新该状态。

  • useRef:这个钩子提供了一种直接引用 DOM 元素的方法。它对于访问和操作元素而不触发重新渲染非常有用。在我们的例子中,我们将使用 useRef 来检查点击是否发生在下拉菜单之外。

逐步实施

第 1 步:设置项目

首先,确保您有一个使用 Tailwind CSS 设置的 React 项目。如果没有,可以使用 Create React App 创建它:

npx create-react-app my-dropdown-app
cd my-dropdown-app
npm install tailwindcss
npx tailwindcss init

通过将以下行添加到 tailwind.config.js 来配置 Tailwind:

module.exports = {
  purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
  darkMode: false,
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};

然后,将 Tailwind 指令添加到您的 index.css 中:

@tailwind base;
@tailwind components;
@tailwind utilities;

第 2 步:安装 Font Awesome

要使用Font Awesome图标,您需要安装@fortawesome/react-fontawesome包:

npm install @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons

第三步:创建导航栏组件

在 src 目录中,创建一个名为 Navbar.tsx 的新文件。该组件将包含下拉菜单。

实施导航栏代码

这是 Navbar 组件的代码,它利用 useReducer 和 useRef 来处理下拉状态和外部点击。

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(null);
  const notificationDropdownRef = useRef(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 (
    
); }; export default Navbar;

第 4 步:将导航栏集成到您的应用程序中

打开您的 App.tsx 文件并导入 Navbar 组件以将其包含在您的应用程序布局中。

import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import Navbar from './components/Navbar';

const App: React.FC = () => {
  return (
    
      
      

Welcome to DC Dashboard!

{/* Other components and content */}
); }; export default App;

第 5 步:使用 Tailwind CSS 进行样式设置

Tailwind CSS 提供的类应该已经给出了简洁的设计。但是,请随意根据需要自定义样式。

第 6 步:测试应用程序

通过运行以下命令启动您的应用程序:

bash
npm start

您现在应该在应用程序顶部看到一个导航栏,其中包含通知和用户配置文件设置的功能下拉菜单。

常问问题

1。本例中 useReducer 钩子如何工作?

useReducer 钩子允许我们有效地管理多个下拉菜单的状态。我们定义一个reducer函数,它接受当前状态和返回新状态的操作。此模式有助于切换下拉菜单和处理立即关闭所有下拉菜单的逻辑。

2.为什么使用useRef?

我们使用 useRef 来引用下拉元素。这让我们可以检查点击事件是否发生在这些元素之外。如果是这样,我们会发送一个操作来关闭下拉菜单,以确保干净的用户体验。

3.我可以添加更多下拉菜单吗?

是的!您可以扩展减速器中的状态并类似地添加更多下拉菜单。只需确保每个下拉菜单都有自己的引用和切换功能。

4。此实现是否需要 Tailwind CSS?

不,Tailwind CSS 不是强制性的。您可以使用任何 CSS 框架或自定义 CSS 样式来设置下拉菜单的样式,但 Tailwind 使样式设置更快、响应更灵敏。

结论

在本指南中,您学习了如何使用 useReducer 进行状态管理和 useRef 处理外部点击,在 React 中创建功能性下拉菜单。这种方法提供了一种干净高效的方式来管理复杂的 UI 交互,从而增强整体用户体验。您可以在此基础上随意构建并自定义它以满足您的应用程序的需求!

版本声明 本文转载于:https://dev.to/chintanonweb/smart-dropdowns-in-react-using-usereducer-and-useref-for-outside-click-handling-138h?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-07-08
  • Java中假唤醒真的会发生吗?
    Java中假唤醒真的会发生吗?
    在Java中的浪费唤醒:真实性或神话?在Java同步中伪装唤醒的概念已经是讨论的主题。尽管存在这种行为的潜力,但问题仍然存在:它们实际上是在实践中发生的吗? Linux的唤醒机制根据Wikipedia关于伪造唤醒的文章,linux实现了pthread_cond_wait()功能的Linux实现,利用...
    编程 发布于2025-07-08
  • 如何处理PHP文件系统功能中的UTF-8文件名?
    如何处理PHP文件系统功能中的UTF-8文件名?
    在PHP的Filesystem functions中处理UTF-8 FileNames 在使用PHP的MKDIR函数中含有UTF-8字符的文件很多flusf-8字符时,您可能会在Windows Explorer中遇到comploreer grounder grounder grounder gro...
    编程 发布于2025-07-08
  • 为什么尽管有效代码,为什么在PHP中捕获输入?
    为什么尽管有效代码,为什么在PHP中捕获输入?
    在php ;?>" method="post">The intention is to capture the input from the text box and display it when the submit button is clicked.但是,输出...
    编程 发布于2025-07-08
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python Matplotlib's path.contains_points FunctionMatplotlib's path.contains_points function employs a path object to represent the polygon.它...
    编程 发布于2025-07-08
  • 如何从PHP中的数组中提取随机元素?
    如何从PHP中的数组中提取随机元素?
    从阵列中的随机选择,可以轻松从数组中获取随机项目。考虑以下数组:; 从此数组中检索一个随机项目,利用array_rand( array_rand()函数从数组返回一个随机键。通过将$项目数组索引使用此键,我们可以从数组中访问一个随机元素。这种方法为选择随机项目提供了一种直接且可靠的方法。
    编程 发布于2025-07-08
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-07-08
  • Java中如何使用观察者模式实现自定义事件?
    Java中如何使用观察者模式实现自定义事件?
    在Java 中创建自定义事件的自定义事件在许多编程场景中都是无关紧要的,使组件能够基于特定的触发器相互通信。本文旨在解决以下内容:问题语句我们如何在Java中实现自定义事件以促进基于特定事件的对象之间的交互,定义了管理订阅者的类界面。以下代码片段演示了如何使用观察者模式创建自定义事件: args)...
    编程 发布于2025-07-08
  • 如何高效地在一个事务中插入数据到多个MySQL表?
    如何高效地在一个事务中插入数据到多个MySQL表?
    mySQL插入到多个表中,该数据可能会产生意外的结果。虽然似乎有多个查询可以解决问题,但将从用户表的自动信息ID与配置文件表的手动用户ID相关联提出了挑战。使用Transactions和last_insert_id() 插入用户(用户名,密码)值('test','test...
    编程 发布于2025-07-08
  • 您如何在Laravel Blade模板中定义变量?
    您如何在Laravel Blade模板中定义变量?
    在Laravel Blade模板中使用Elegance 在blade模板中如何分配变量对于存储以后使用的数据至关重要。在使用“ {{}}”分配变量的同时,它可能并不总是最优雅的解决方案。幸运的是,Blade通过@php Directive提供了更优雅的方法: $ old_section =“...
    编程 发布于2025-07-08
  • Java字符串非空且非null的有效检查方法
    Java字符串非空且非null的有效检查方法
    检查字符串是否不是null而不是空的 if(str!= null && str.isementy())二手: if(str!= null && str.length()== 0) option 3:trim()。isement(Isement() trim whitespace whitesp...
    编程 发布于2025-07-08
  • 如何简化PHP中的JSON解析以获取多维阵列?
    如何简化PHP中的JSON解析以获取多维阵列?
    php 试图在PHP中解析JSON数据的JSON可能具有挑战性,尤其是在处理多维数组时。 To simplify the process, it's recommended to parse the JSON as an array rather than an object.To do...
    编程 发布于2025-07-08
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-07-08
  • 版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    版本5.6.5之前,使用current_timestamp与时间戳列的current_timestamp与时间戳列有什么限制?
    在时间戳列上使用current_timestamp或MySQL版本中的current_timestamp或在5.6.5 此限制源于遗留实现的关注,这些限制需要对当前的_timestamp功能进行特定的实现。 创建表`foo`( `Productid` int(10)unsigned not n...
    编程 发布于2025-07-08
  • MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    在两个条件下插入或更新或更新 solution:的答案在于mysql的插入中...在重复键更新语法上。如果不存在匹配行或更新现有行,则此功能强大的功能可以通过插入新行来进行有效的数据操作。如果违反了唯一的密钥约束。实现所需的行为,该表必须具有唯一的键定义(在这种情况下为'名称'...
    编程 发布于2025-07-08

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3