我正在尝试在打开和关闭组件时添加和删除寡妇事件侦听器。
问题是处理程序中的isOpen
始终处于初始状态并且不使用最新状态。我该如何解决这个问题?我见过很多使用
useEffect(() => { .. }, []);
的解决方案,但这行不通,因为我希望能够在安装和卸载时的不同时间添加和删除侦听器。我也尝试过使用
useCallback
,但它有与
useEffect
相同的问题
这是我当前的代码:
import { forwardRef, Ref, useEffect, useImperativeHandle, useRef } from 'react';
import { useState } from 'react';
type Props = {
};
export const Popover = forwardRef((props: Props, ref: Ref<any>) => {
const [isOpen, setIsOpen] = useState(false);
const [clickOutsideHandler] = useState(() =>
(evt: MouseEvent): void => {
console.log('!isOpen: ' + isOpen); // <-- issue is that `isOpen` is always false
if (!isOpen || popoverRef.current.contains(evt.target as Node)) { return; } // Clicked on one of the own children
close();
}
);
const popoverRef = useRef(null);
const open = (): void => {
if (isOpen) { return; }
setIsOpen(true);
}
const close = (): void => {
if (!isOpen) { return; }
setIsOpen(false);
}
useImperativeHandle(ref, () => ({
open,
}));
useEffect(() => {
if (isOpen) {
window.requestAnimationFrame(() => { // Wait 1 tick; otherwise a potential click that opend the element will immediately trigger a click-outside event
document.addEventListener('click', clickOutsideHandler);
});
}
else {
document.removeEventListener('click', clickOutsideHandler);
}
}, [isOpen]);
return (
<>
{(isOpen) &&
<div ref={popoverRef}>
<div onClick={() => { close(); }}>Some Content here</div>
</div>
}
</>
);
});
问题是:如何使处理程序保留,同时仍然能够访问其中的正确状态?
clickOutsideHandler
函数定义为
useState
,您应该将其设为实际函数,因为它不使用任何状态逻辑。
const clickOutsideHandler = (evt) => {
if (!isOpen || popoverRef.current.contains(evt.target as Node)) { return; } // Clicked on one of the own children
close();
};