Выпадающее меню React снова открывает меню при нажатии снаружи

У меня есть раскрывающийся список в дочернем компоненте, и все работает отлично, пока я не реализовал внешний щелчок, чтобы закрыть раскрывающийся список.

Даже на этом внешнем щелчке, чтобы закрыть, работает отлично, но нажатие на меню, чтобы закрыть, не работает, как ожидалось.

<div>
 <button onClick = {handleAboutMenuActive}>About</button>
 {aboutMenuActive && (
   <Dropdown
      onClickOutside = {() => {
         handleAboutMenuActive();
      }}
   >
   </Dropdown>
 )}
</div>

Вот ссылка на stackblitz https://stackblitz.com/edit/react-ubugsj?file=src%2FApp.js

Решение, которое я могу придумать сейчас, это добавить setTimeout к обратному вызову внешнего клика, но все, что меньше 150, ненадежно, поэтому мне нужно другое и работающее решение этой проблемы.

onClickOutside = {() => {
  setTimeout(() => {
    handleAboutMenuActive();
  }, 150);
}}

Вот ссылка на рабочее решение https://stackblitz.com/edit/react-rpqux7?file=src%2FApp.js,src%2FDropdown.js

Мой вопрос будет заключаться в том, что мой подход к решению этой проблемы правильный и окончательный, или вы рекомендуете какое-либо лучшее решение, чем использование setTimeout?

Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в первой демонстрации заключается в том, что триггеры handleClickOutside срабатывают независимо от того, где вы нажали. Когда вы нажимаете кнопку About, функция handleAboutMenuActive срабатывает дважды.

На мой взгляд, лучший способ реализовать этот эффект — проверить, куда щелкнул пользователь. Мы можем использовать useRef.

Во-первых, добавьте ref для кнопки в App.js и передайте ее в качестве реквизита дочернему компоненту Dropdown:

import React, { useState, useRef } from 'react';
// some other code
export default function App() {
  const buttonRef = useRef();
  // some other code
  return (
    <div>
      <button ref = {buttonRef} onClick = {handleAboutMenuActive}>About</button>
      {aboutMenuActive && (
        <Dropdown
          buttonRef = {buttonRef}
          onClickOutside = {() => {
            handleAboutMenuActive();
          }}
        ></Dropdown>
      )}
    </div>
  )
}

Во-вторых, добавьте еще один ref для всего элемента ul и проверяйте оба ref всякий раз, когда срабатывает handleClickOutside.

import React, { useEffect, useRef } from 'react';
export default function Dropdown({ buttonRef, onClickOutside }) {
  const listRef = useRef()

  useEffect(() => {
    function handleClickOutside(event) {
      // refs will exist after the elements are rendered.
      // this condition prevents the refs were still undefined.
      if (!listRef.current || !buttonRef.current) {
        return;
      }
      // check if user clicked on the button
      if (buttonRef.current.contains(event.target)) {
        return;
      }
      // check if user clicked on the list
      if (listRef.current.contains(event.target)) {
        return;
      }
      onClickOutside();
    }
    // some other code
  })

  return (
    <ul ref = {listRef}>
      <li>Company</li>
      <li>Contact</li>
    </ul>
  );
}

Другие вопросы по теме