You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Next.js中实现类似Sonner的DeleteModal组件?

在Next.js中实现全局可调用的DeleteModal组件

要实现类似Sonner的「全局渲染组件+函数调用触发」的效果,核心思路是通过全局状态管理+监听机制,让模态框的状态在组件间共享,同时提供简洁的调用函数。以下是具体实现步骤:

1. 创建全局状态管理模块

这个模块负责维护模态框的状态(显示/隐藏、提示文本、确认回调),并提供触发和关闭的函数,同时支持组件监听状态变化。

// app/delete-modal/store.js
'use client';
import React from 'react';

// 全局状态变量
let isOpen = false;
let confirmCallback = () => {};
let message = '';
// 存储状态变化的监听函数
let listeners = [];

// 通知所有监听者更新状态
const notifyListeners = () => {
  listeners.forEach(listener => listener({ isOpen, confirmCallback, message }));
};

// 触发模态框显示的函数,外部组件直接调用
export const showDeleteModal = (options) => {
  isOpen = true;
  message = options.message || '确定要删除吗?';
  confirmCallback = options.onConfirm || (() => {});
  notifyListeners();
};

// 关闭模态框的函数
export const hideDeleteModal = () => {
  isOpen = false;
  notifyListeners();
};

// 自定义Hook,让组件和全局状态同步
export const useDeleteModal = () => {
  const [state, setState] = React.useState({ isOpen, confirmCallback, message });

  React.useEffect(() => {
    // 注册监听函数
    const listener = (newState) => setState(newState);
    listeners.push(listener);
    // 组件卸载时移除监听
    return () => {
      listeners = listeners.filter(l => l !== listener);
    };
  }, []);

  return {
    ...state,
    hideDeleteModal,
  };
};

2. 实现DeleteModal组件

这个组件通过自定义Hook获取全局状态,根据状态决定是否渲染,并处理确认/取消逻辑。

// app/components/DeleteModal.jsx
'use client';
import { useDeleteModal } from '../delete-modal/store';

export default function DeleteModal() {
  const { isOpen, message, confirmCallback, hideDeleteModal } = useDeleteModal();

  // 状态为关闭时不渲染
  if (!isOpen) return null;

  const handleConfirm = () => {
    // 执行传入的确认回调
    confirmCallback();
    // 关闭模态框
    hideDeleteModal();
  };

  return (
    <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
      <div className="bg-white rounded-lg p-6 w-96 shadow-xl">
        <h3 className="text-lg font-semibold mb-4">确认删除</h3>
        <p className="text-gray-700 mb-6">{message}</p>
        <div className="flex justify-end gap-3">
          <button
            onClick={hideDeleteModal}
            className="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
          >
            取消
          </button>
          <button
            onClick={handleConfirm}
            className="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors"
          >
            删除
          </button>
        </div>
      </div>
    </div>
  );
}

3. 在Layout中全局挂载模态框

在根布局中渲染一次DeleteModal,这样它就会在整个应用的生命周期中存在,无需在其他组件重复引入。

// app/layout.jsx
import DeleteModal from './components/DeleteModal';

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN">
      <body className="min-h-screen">
        {children}
        {/* 全局模态框,只需要渲染一次 */}
        <DeleteModal />
      </body>
    </html>
  );
}

4. 在任意组件中调用触发

在需要触发删除确认的组件中,直接导入showDeleteModal函数调用即可,无需再处理模态框的渲染逻辑。

// app/components/ItemCard.jsx
'use client';
import { showDeleteModal } from '../delete-modal/store';

export default function ItemCard({ itemId, itemName }) {
  const handleDeleteItem = () => {
    showDeleteModal({
      message: `确定要删除「${itemName}」吗?此操作不可恢复。`,
      onConfirm: async () => {
        // 这里编写实际的删除逻辑,比如调用API
        console.log(`删除ID为${itemId}的项`);
        // 可以在这里添加删除后的提示或页面更新逻辑
      },
    });
  };

  return (
    <div className="border p-4 rounded-lg flex justify-between items-center">
      <span>{itemName}</span>
      <button
        onClick={handleDeleteItem}
        className="text-red-600 hover:text-red-800 font-medium"
      >
        删除
      </button>
    </div>
  );
}

关键说明

  • 客户端组件标记:所有涉及状态交互的组件(DeleteModal、调用showDeleteModal的组件)都需要添加'use client'指令,因为Next.js App Router默认是服务器组件,无法处理客户端状态。
  • 状态同步机制:通过监听函数数组,让全局状态的变化能同步到DeleteModal组件,确保模态框能正确响应显示/隐藏操作。
  • 灵活性:可以根据需求扩展showDeleteModal的参数,比如添加取消回调、自定义按钮文本等。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 06:33:15