You need to enable JavaScript to run this app. React可复用Cupertino-Pane组件嵌套时如何感知遮罩点击同步显示状态
优惠活动
大模型
产品
解决方案
定价
更多

React可复用Cupertino-Pane组件嵌套时如何感知遮罩点击同步显示状态

问题定位

当前的组件状态流存在断连:

  • show状态的唯一持有方是父组件Feed,通过prop单向传入IonDrawer控制显隐
  • 但IonDrawer内部触发的遮罩点击、底部下滑关闭、快速滑动关闭等行为,只调用了CupertinoPane的原生隐藏方法,没有把“面板已关闭”的信号回传给父组件
  • 最终就会出现:视觉上面板已经消失,但父组件的show值仍然是true,下次点击按钮时因为状态没有从false到true的跳转,会出现面板不弹出、状态错乱的问题
修复方案

1. 给IonDrawer增加向上回传关闭事件的能力

在原有组件基础上新增onClose回调prop,所有触发面板关闭的场景,都主动调用这个回调通知父组件更新状态。同时注意规避React闭包的常见坑:CupertinoPane实例只在组件挂载时初始化一次,直接绑定初始props的话,后续props更新永远拿不到最新的回调,需要用ref存最新的回调值。
修改后的完整组件代码:

import { CupertinoPane } from "cupertino-pane";
import { useEffect, useRef } from "react";
import { PanelProps } from "../../interfaces";

// 补充props类型定义
interface PanelProps {
  panelKey: string;
  show: boolean;
  children: React.ReactNode;
  onClose?: () => void;
}

const IonDrawer = ({ panelKey, show, children, onClose }: PanelProps) => {
  const drawerRef = useRef<CupertinoPane>();
  // 存储最新的onClose回调,避免闭包捕获旧值
  const onCloseRef = useRef(onClose);
  useEffect(() => {
    onCloseRef.current = onClose;
  }, [onClose]);

  const hidePanel = async () => {
    await drawerRef.current?.hide();
    // 面板完成隐藏后,通知父组件更新状态
    onCloseRef.current?.();
  };

  useEffect(() => {
    drawerRef.current = new CupertinoPane(`.${panelKey}`, {
      backdrop: true,
      bottomClose: true,
      buttonClose: false,
      parentElement: "body",
      fastSwipeClose: true,
      fitHeight: true,
      // 所有内置关闭事件统一走hidePanel逻辑
      onBackdropTap: () => hidePanel(),
      onBottomClose: () => hidePanel(),
      onFastSwipeClose: () => hidePanel(),
    });

    // 组件卸载时销毁实例,避免多实例下的残留DOM和内存泄漏
    return () => {
      drawerRef.current?.destroy();
    };
  }, [panelKey]);

  // 同步外部传入的show状态变化
  useEffect(() => {
    if (show) {
      drawerRef.current?.present({ animate: true });
    } else {
      hidePanel();
    }
  }, [show]);

  return <div className={panelKey}>{children}</div>;
};

export default IonDrawer;

2. 父组件接入关闭回调

在Feed组件调用IonDrawer时,传入onClose回调,触发时把自身的show状态切回false:

const Feed: FC<FeedProps> = ({ panelKey }) => {
  const [show, setShow] = useState<boolean>(false);

  return (
    <>
      <IonDrawer 
        show={show} 
        panelKey={panelKey}
        onClose={() => setShow(false)}
      >
        <h3>John Doe</h3>
      </IonDrawer>

      <IonButton 
        className="btn" 
        fill="clear" 
        color="secondary"
        onClick={() => setShow(true)}
      >
        <div className="d-flex align-center">
          <IoEllipsisHorizontalCircleOutline size="20" />
        </div>
      </IonButton>
    </>
  );
};
落地效果
  • 点击按钮时,setShow(true)触发IonDrawer的effect,弹出面板
  • 触发遮罩点击、底部拖拽关闭、快速滑动关闭时,IonDrawer会先调用原生hide方法关闭面板,再触发onClose回调把父组件的show切回false
  • 多Feed实例场景下,每个实例持有独立的show状态,通过唯一的panelKey绑定对应面板,不会出现交叉干扰

内容的提问来源于stack exchange,提问作者AbdulAzeez Olanrewaju

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.26 21:27:04