У меня есть раскрывающийся список в дочернем компоненте, и все работает отлично, пока я не реализовал внешний щелчок, чтобы закрыть раскрывающийся список.
Даже на этом внешнем щелчке, чтобы закрыть, работает отлично, но нажатие на меню, чтобы закрыть, не работает, как ожидалось.
<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?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Проблема в первой демонстрации заключается в том, что триггеры 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>
);
}