Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

5.x Popover 的 onOpenChange 触发的时机存在问题 #42924

Closed
linxianxi opened this issue Jun 8, 2023 · 18 comments · May be fixed by react-component/select#951
Closed

5.x Popover 的 onOpenChange 触发的时机存在问题 #42924

linxianxi opened this issue Jun 8, 2023 · 18 comments · May be fixed by react-component/select#951

Comments

@linxianxi
Copy link
Contributor

linxianxi commented Jun 8, 2023

Reproduction link

Edit on CodeSandbox

4.x 是可以的
Edit on CodeSandbox

Steps to reproduce

目的是为了在 Popover 内打开 Modal 时,Popover 不关闭

1、打开 Popover
2、打开 Modal
3、关闭 Modal

What is expected?

Popover 是打开的。且在 Modal 打开的时候点击任意位置不应触发 onOpenChange。

What is actually happening?

Popover 关闭了。且在 Modal 打开的时候点击任意位置都会触发 onOpenChange。

Environment Info
antd 5.6.1
React lastest
System mac
Browser chrome

discussion #42863 (reply in thread)

@linxianxi linxianxi changed the title 5.x 通过受控模式来控制 Popover 内打开 Modal 后,且不关闭 Popover,存在问题 5.x Popover 的 onOpenChange 触发的时机存在问题 Jun 8, 2023
@BoyYangzai
Copy link
Contributor

BoyYangzai commented Jun 8, 2023

@linxianxi 考虑使用 受控 + useClickOutside 解决呢
应该也挺方便的 传入 ModelRef 和 PopoverRef 数组

import { MutableRefObject, useCallback, useEffect, useRef } from 'react';

const defaultEvent = 'click';
type RefType = Element | (() => Element | null) | null | undefined;

const useClickOutside = <T extends Element>(
  dom: RefType | RefType[],
  callback: (event: Event) => void,
  eventName: string = defaultEvent,
  listenerOptions?: boolean | AddEventListenerOptions
) => {
  const callbackRef = useRef(callback);
  const targetRefs = useRef<RefType[]>([]);
  const element = useRef<T>();

  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  useEffect(() => {
    targetRefs.current = Array.isArray(dom) ? dom : [dom];
  }, [dom]);

  const eventHandler = useCallback(
    (e: Event) => {
      const isOutside = targetRefs.current.every((item) => {
        const el = typeof item === 'function' ? item() : item || element.current;
        return !el?.contains(e.target as Node);
      });

      if (isOutside) {
        callbackRef.current(e);
      }
    },
    [element.current, targetRefs.current]
  );

  useEffect(() => {
    document.addEventListener(eventName, eventHandler, listenerOptions);
    return () => {
      document.removeEventListener(eventName, eventHandler, listenerOptions);
    };
  }, [eventName, listenerOptions, eventHandler]);

  return element as MutableRefObject<T>;
};

export default useClickOutside;

@linxianxi
Copy link
Contributor Author

怎么实现不谈,这个 issue 谈的是 bug。

@linxianxi
Copy link
Contributor Author

@BoyYangzai 你可以在 codesandbox 尝试用 useClickAway 实现一下

@BoyYangzai
Copy link
Contributor

@linxianxi 晚点下班看看

@BoyYangzai
Copy link
Contributor

BoyYangzai commented Jun 8, 2023

@linxianxi @vaynevayne 简单实现了一下 拿不到PopOver和Modal的真实dom的ref有点难顶
https://codesandbox.io/s/ji-ben-antd-5-6-0-forked-63cy75?file=/demo.tsx
@zombieJ 稍微有点复杂 豆酱你怎么看
如果这里的 ref 能拿到 popover 的 ref 就简单很多了 我也要加入支持没有 blur 的一方了(狗头🤪
image

@linxianxi
Copy link
Contributor Author

不一定要 ref,document.querySelector 就能获取。还是重点关注 5.x 和 4.x 行为不同的问题吧

@vaynevayne
Copy link

内部处理onBlur的代码在哪里啊?能贴个连接吗 分析一波

@zombieJ
Copy link
Member

zombieJ commented Jun 8, 2023

v4 的 Popover 底层监听的是 mouseDown 事件,v5 监听的是 click 事件,所以 popover change 和 modal change 时序是反的。有点不记得为啥这么调整了。

@linxianxi
Copy link
Contributor Author

linxianxi commented Jun 9, 2023

v5 现在实现这种需求最简单的方式应该是这样吧,但是感觉也不是很优雅。
https://codesandbox.io/s/ji-ben-antd-5-6-0-forked-q5m93q

@vaynevayne
Copy link

vaynevayne commented Jun 9, 2023

popover 换成 select 试试
写了个 demo : https://stackblitz.com/edit/react-djm2de?file=index.tsx

@linxianxi
Copy link
Contributor Author

@linxianxi
Copy link
Contributor Author

@zombieJ 也没比较好的办法了吧,要不要在文档里加个 demo?

@vaynevayne
Copy link

https://stackblitz.com/edit/react-djm2de?file=demo.tsx 弹窗打开时, 点一下这个例子里的modal , popover 就就关闭了, 你那个例子里的阻止关闭, 关键代码是什么?

@vaynevayne
Copy link

react-component/select#943 这个原因? @zombieJ

@zombieJ
Copy link
Member

zombieJ commented Jun 12, 2023

react-component/select#943 这个原因? @zombieJ

Popover 组件和 Select 组件没有关系。Select 底层没用 Popover,Popover 底层也没用 Select。

@vaynevayne
Copy link

react-component/select#943 这个原因? @zombieJ

Popover 组件和 Select 组件没有关系。Select 底层没用 Popover,Popover 底层也没用 Select。

知道了, 没关系, 后面我不是找到原因还在 rc-select 里提了 pr 嘛, 怎么还看这个啊

@zombieJ
Copy link
Member

zombieJ commented Jun 14, 2023

v5 现在实现这种需求最简单的方式应该是这样吧,但是感觉也不是很优雅。 https://codesandbox.io/s/ji-ben-antd-5-6-0-forked-q5m93q

也是这个思路,effect 延迟掉 visible。

@linxianxi
Copy link
Contributor Author

因 rc-trigger 内部 useWinClick 改成捕获了,所以现在不需要 delay 来处理了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants