React 进阶:一个优雅的弹窗管理 Hook 实现

前言

在 React 项目中,弹窗管理一直是一个常见的需求。传统的弹窗管理方式往往需要在组件中维护多个 state,代码繁琐且不易维护。今天分享一个基于 Hook 的弹窗管理方案,让弹窗管理变得更加优雅。

实现目标

  1. 简化弹窗的打开和关闭操作
  2. 支持动态传递参数
  3. 自动销毁不需要的弹窗实例

核心实现

tsx 复制代码
import { useMemoizedFn } from "ahooks";
import React, { cloneElement, isValidElement, useRef, useState } from "react";

export type DialogProps = { onOpenChange: (open: boolean) => void; open: boolean,afterClose: ()=> void };

/**
 * 弹窗
 * @description 通过hook管理弹窗
 * @example
 * const [dialog, dialogContextHolder] = useDialog(Com)
 * 
 * dialog.show({})
 * dialog.close()
 * 
 * return (<>{dialogContextHolder}</>)
 */
export function useDialog<T = object>(dialogRender: any) {
  const [open, setOpen] = useState(false);
  const [destroyDialogRender,setDestroyDialogRender] = useState(true)
  const propsRef = useRef<T | null>(null);

  const onOpenChange = useMemoizedFn((open: boolean)=>{
    setOpen(open)
  })

  const afterClose = useMemoizedFn(() => {
    setDestroyDialogRender(true)
  })

  const show = useMemoizedFn((props?: T) => {
    propsRef.current = props ?? null;
    setDestroyDialogRender(false)
    onOpenChange(true);
  });
  const close = useMemoizedFn(() => {
    propsRef.current = null;
    onOpenChange(false);
  });

  const memoizedDialogRender = useMemoizedFn(() => {
    const props = {
      ...propsRef.current,
      open,
      onOpenChange,
      afterClose
    } as DialogProps & T;
    
    try {
      if (isValidElement(dialogRender)) {
        return cloneElement(dialogRender, props);
      }
      if (typeof dialogRender === "function") {
        return React.createElement(dialogRender, props);
      }
      console.error("dialogRender must be a ReactElement or a function");
      return null;
    } catch (error) {
      console.error("Dialog render failed:", error);
      return null;
    }
  });
  const dialogContextHolder = destroyDialogRender ? null :  memoizedDialogRender();
  return [
    {
      show,
      close,
      open,
    },
    dialogContextHolder,
  ];
}

核心方法解析

  1. 显示和关闭方法
tsx 复制代码
const show = useMemoizedFn((props: T) => {
  propsRef.current = props;
  setDestroyDialogRender(false);
  onOpenChange(true);
});

const close = useMemoizedFn(() => {
  propsRef.current = null;
  onOpenChange(false);
});
  1. 弹窗渲染逻辑
tsx 复制代码
const memoizedDialogRender = useMemoizedFn(() => {
  const props = {
    ...propsRef.current,
    open,
    onOpenChange,
    afterClose
  } as DialogProps & T;
  
  if (isValidElement(dialogRender)) {
    return cloneElement(dialogRender, props);
  }
  if (typeof dialogRender === "function") {
    return React.createElement(dialogRender, props);
  }
  return null;
});

使用示例

tsx 复制代码
import { useDialog,afterClose, type DialogProps } from './useDialog';
import React,{ FC } from 'react';
import { Button, Modal } from 'antd';

// 弹窗组件
const MyDialog:FC<{name: string} & DialogProps> = ({ open, onOpenChange,afterClose, name }) => (
  <Modal open={open} onCancel={() => onOpenChange(false)} afterClose={afterClose}>
    <div>Hello, {name}!</div>
  </Modal>
);

// 使用 Hook
const MyComponent = () => {
  const [dialog, dialogHolder] = useDialog<{name: string}>(MyDialog);

  const handleClick = () => {
    dialog.show({ name: 'React' });
  };

  return (
    <>
      <Button onClick={handleClick}>打开弹窗</Button>
      {dialogHolder}
    </>
  );
};

通过这个 Hook,我们可以用更优雅的方式管理弹窗状态。如果觉得文章有帮助,欢迎点赞转发!

相关推荐
Up九五小庞6 分钟前
开源埋点分析平台 ClkLog 本地部署 + Web JS 埋点测试实战--九五小庞
前端·javascript·开源
摘星编程30 分钟前
React Native + OpenHarmony:UniversalLink通用链接
javascript·react native·react.js
qq_177767371 小时前
React Native鸿蒙跨平台数据使用监控应用技术,通过setInterval每5秒更新一次数据使用情况和套餐使用情况,模拟了真实应用中的数据监控场景
开发语言·前端·javascript·react native·react.js·ecmascript·harmonyos
烬头88211 小时前
React Native鸿蒙跨平台应用实现了onCategoryPress等核心函数,用于处理用户交互和状态更新,通过计算已支出和剩余预算
前端·javascript·react native·react.js·ecmascript·交互·harmonyos
2601_949593653 小时前
基础入门 React Native 鸿蒙跨平台开发:卡片组件
react native·react.js·harmonyos
天人合一peng3 小时前
Unity中button 和toggle监听事件函数有无参数
前端·unity·游戏引擎
方也_arkling4 小时前
别名路径联想提示。@/统一文件路径的配置
前端·javascript
毕设源码-朱学姐4 小时前
【开题答辩全过程】以 基于web教师继续教育系统的设计与实现为例,包含答辩的问题和答案
前端
qq_177767374 小时前
React Native鸿蒙跨平台剧集管理应用实现,包含主应用组件、剧集列表、分类筛选、搜索排序等功能模块
javascript·react native·react.js·交互·harmonyos
qq_177767374 小时前
React Native鸿蒙跨平台自定义复选框组件,通过样式数组实现选中/未选中状态的样式切换,使用链式调用替代样式数组,实现状态驱动的样式变化
javascript·react native·react.js·架构·ecmascript·harmonyos·媒体