React Hooks:模态事件监听器的useEffect

React Hooks: useEffect for modal event listener

我有一个模态对话框,如果用户在模态之外单击,我想将其关闭。我已经编写了以下 useEffect 代码,但我 运行 进入以下问题:

模态对话框包含许多子项(React 节点),这些子项可能会更改(例如,用户删除列表的条目)。这些交互触发了我的 onClick 方法,但由于已从模态中删除了单击的列表项,即使单击在模态内,模态也会关闭。

我认为在 useEffect 的第二个参数中添加 [children] 会足够快地清理旧的效果事件侦听器,以至于该方法不会再次 运行,但事实并非如此。

我在具有 ignoreNextClick 状态的 class 组件中处理了同样的问题,但必须有更清洁的解决方案,对吗?

    useEffect( () => {
        const onClick = ( event ) => {
            const menu = document.getElementById( 'singleton-modal' );
            if ( !menu ) return;

            // do not close menu if user clicked inside
            const targetInMenu = menu.contains( event.target );
            const targetIsMenu = menu === event.target;
            if ( targetInMenu || targetIsMenu ) return;

            onCloseModal();
        };

        window.addEventListener( 'click', onClick, false );

        return () => window.removeEventListener( 'click', onClick, false );
    }, [ children ] );

您可以从 event.target 检查模态的父级。 如果当前目标在模态内,则 return.

您可以使用 closest 来做到这一点。

请参阅以下解决方案。

...

    if (event.target.closest( '.singleton-modal' ) || event.target.classList.contains('singleton-modal')) {
       return;
    }

...

我找到了一个不需要任何存储旧道具的解决方案。

useEffect 调用如下所示:

useEffect( () => {
        const onClickOutside = () => onCloseModal();
        window.addEventListener( 'click', onClickOutside, false );
        return () => window.removeEventListener( 'click', onClickOutside );
    }, [] );

直接将以下点击侦听器添加到模式将阻止 window click-listener 在用户单击模式内部时被调用。

<div
    className={`modal ${ classes }`}
    onClick={event => event.stopPropagation()}
    role="presentation"
>
   {children}
</div>`

我还添加了角色介绍,使模式更易于访问 aria-conform。