」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > React 中的智慧型下拉選單:使用 useReducer 和 useRef 進行外部點擊處理

React 中的智慧型下拉選單:使用 useReducer 和 useRef 進行外部點擊處理

發佈於2024-11-03
瀏覽:620

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如有侵犯,請洽study_golang@163 .com刪除
最新教學 更多>
  • 為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    為什麼不````''{margin:0; }`始終刪除CSS中的最高邊距?
    在CSS 問題:不正確的代碼: 全球範圍將所有餘量重置為零,如提供的代碼所建議的,可能會導致意外的副作用。解決特定的保證金問題是更建議的。 例如,在提供的示例中,將以下代碼添加到CSS中,將解決餘量問題: body H1 { 保證金頂:-40px; } 此方法更精確,避免了由全局保證金重置...
    程式設計 發佈於2025-04-07
  • 如何在其容器中為DIV創建平滑的左右CSS動畫?
    如何在其容器中為DIV創建平滑的左右CSS動畫?
    通用CSS動畫,用於左右運動 ,我們將探索創建一個通用的CSS動畫,以向左和右移動DIV,從而到達其容器的邊緣。該動畫可以應用於具有絕對定位的任何div,無論其未知長度如何。 問題:使用左直接導致瞬時消失 更加流暢的解決方案:混合轉換和左 [並實現平穩的,線性的運動,我們介紹了線性的轉換。...
    程式設計 發佈於2025-04-07
  • 如何從PHP中的Unicode字符串中有效地產生對URL友好的sl。
    如何從PHP中的Unicode字符串中有效地產生對URL友好的sl。
    為有效的slug生成首先,該函數用指定的分隔符替換所有非字母或數字字符。此步驟可確保slug遵守URL慣例。隨後,它採用ICONV函數將文本簡化為us-ascii兼容格式,從而允許更廣泛的字符集合兼容性。 接下來,該函數使用正則表達式刪除了不需要的字符,例如特殊字符和空格。此步驟可確保slug僅包...
    程式設計 發佈於2025-04-07
  • 如何在GO編譯器中自定義編譯優化?
    如何在GO編譯器中自定義編譯優化?
    在GO編譯器中自定義編譯優化 GO中的默認編譯過程遵循特定的優化策略。 However, users may need to adjust these optimizations for specific requirements.Optimization Control in Go Compi...
    程式設計 發佈於2025-04-07
  • 如何有效地選擇熊貓數據框中的列?
    如何有效地選擇熊貓數據框中的列?
    在處理數據操作任務時,在Pandas DataFrames 中選擇列時,選擇特定列的必要條件是必要的。在Pandas中,選擇列的各種選項。 選項1:使用列名 如果已知列索引,請使用ILOC函數選擇它們。請注意,python索引基於零。 df1 = df.iloc [:,0:2]#使用索引0和1 ...
    程式設計 發佈於2025-04-07
  • 為什麼不使用CSS`content'屬性顯示圖像?
    為什麼不使用CSS`content'屬性顯示圖像?
    在Firefox extemers屬性為某些圖像很大,&& && && &&華倍華倍[華氏華倍華氏度]很少見,卻是某些瀏覽屬性很少,尤其是特定於Firefox的某些瀏覽器未能在使用內容屬性引用時未能顯示圖像的情況。這可以在提供的CSS類中看到:。 googlepic { 內容:url(&...
    程式設計 發佈於2025-04-07
  • 如何將來自三個MySQL表的數據組合到新表中?
    如何將來自三個MySQL表的數據組合到新表中?
    mysql:從三個表和列的新表創建新表 答案:為了實現這一目標,您可以利用一個3-way Join。 選擇p。 *,d.content作為年齡 來自人為p的人 加入d.person_id = p.id上的d的詳細信息 加入T.Id = d.detail_id的分類法 其中t.taxonomy ...
    程式設計 發佈於2025-04-07
  • 如何使用PHP將斑點(圖像)正確插入MySQL?
    如何使用PHP將斑點(圖像)正確插入MySQL?
    essue VALUES('$this->image_id','file_get_contents($tmp_image)')";This code builds a string in PHP, but the function call fil...
    程式設計 發佈於2025-04-07
  • 可以在純CS中將多個粘性元素彼此堆疊在一起嗎?
    可以在純CS中將多個粘性元素彼此堆疊在一起嗎?
    [2这里: https://webthemez.com/demo/sticky-multi-header-scroll/index.html posite:sticky; sticky; .Sticky-1 {[ top:1em; z-index:1; 1; { display:gr...
    程式設計 發佈於2025-04-07
  • 如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    如何為PostgreSQL中的每個唯一標識符有效地檢索最後一行?
    postgresql:為每個唯一標識符在postgresql中提取最後一行,您可能需要遇到與數據集合中每個不同標識的信息相關的信息。考慮以下數據:[ 1 2014-02-01 kjkj 在數據集中的每個唯一ID中檢索最後一行的信息,您可以在操作員上使用Postgres的有效效率: id dat...
    程式設計 發佈於2025-04-07
  • 如何使用“ JSON”軟件包解析JSON陣列?
    如何使用“ JSON”軟件包解析JSON陣列?
    parsing JSON與JSON軟件包 QUALDALS:考慮以下go代碼:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    程式設計 發佈於2025-04-07
  • 如何使用PHP從XML文件中有效地檢索屬性值?
    如何使用PHP從XML文件中有效地檢索屬性值?
    從php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    程式設計 發佈於2025-04-07
  • 如何在鼠標單擊時編程選擇DIV中的所有文本?
    如何在鼠標單擊時編程選擇DIV中的所有文本?
    在鼠標上選擇div文本單擊帶有文本內容,用戶如何使用單個鼠標單擊單擊div中的整個文本?這允許用戶輕鬆拖放所選的文本或直接複製它。 在單個鼠標上單擊的div元素中選擇文本,您可以使用以下Javascript函數: function selecttext(canduterid){ if(d...
    程式設計 發佈於2025-04-07
  • 如何同步迭代並從PHP中的兩個等級陣列打印值?
    如何同步迭代並從PHP中的兩個等級陣列打印值?
    同步的迭代和打印值來自相同大小的兩個數組使用兩個數組相等大小的selectbox時,一個包含country代碼的數組,另一個包含鄉村代碼,另一個包含其相應名稱的數組,可能會因不當提供了exply for for for the uncore for the forsion for for ytry...
    程式設計 發佈於2025-04-07
  • 如何修復\“常規錯誤:2006 MySQL Server在插入數據時已經消失\”?
    如何修復\“常規錯誤:2006 MySQL Server在插入數據時已經消失\”?
    How to Resolve "General error: 2006 MySQL server has gone away" While Inserting RecordsIntroduction:Inserting data into a MySQL database can...
    程式設計 發佈於2025-04-07

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3