如何实现React全局可调用的自定义Notification工具函数(支持非React场景)
实现可全局调用的Notification工具函数
1. 实现全局队列与工具函数
创建单独文件notificationUtils.js,作为非React场景和React组件的通信桥梁:
// 全局队列,存储待显示的通知 const notificationQueue = []; // 供外部调用的工具函数 export function showNotification(message, onClose) { notificationQueue.push({ id: Date.now(), // 唯一标识每个通知 message, onClose: onClose || (() => {}) }); } export { notificationQueue };
2. 实现顶层Notification容器组件
创建NotificationContainer.jsx,负责渲染通知、处理自动关闭和手动关闭逻辑:
import { useState, useEffect, useRef } from 'react'; import Notification from './Notification'; import { notificationQueue } from './notificationUtils'; export default function NotificationContainer() { const [notifications, setNotifications] = useState([]); const resolveRef = useRef({}); // 存储每个通知的resolve函数,用于手动关闭 useEffect(() => { // 处理队列中的通知 const processQueue = async () => { while (notificationQueue.length > 0) { const notification = notificationQueue.shift(); const { id, message, onClose } = notification; // 将通知加入渲染列表 setNotifications(prev => [...prev, { id, message }]); // 等待5秒或手动关闭 await new Promise(resolve => { resolveRef.current[id] = resolve; // 设置自动关闭定时器 const timer = setTimeout(() => { resolve(); delete resolveRef.current[id]; }, 5000); }); // 移除通知并执行关闭回调 setNotifications(prev => prev.filter(n => n.id !== id)); onClose(); } }; // 初始处理队列 processQueue(); // 监听队列变化,有新通知时重新处理 const observer = new MutationObserver(() => { if (notificationQueue.length > 0) { processQueue(); } }); observer.observe(notificationQueue, { childList: true }); return () => { observer.disconnect(); // 卸载时清除所有待处理通知 Object.values(resolveRef.current).forEach(resolve => resolve()); }; }, []); // 处理手动关闭 const handleClose = (id) => { if (resolveRef.current[id]) { resolveRef.current[id](); } }; return ( <div className="notification-container" style={{ position: 'fixed', top: '20px', right: '20px', zIndex: 9999, display: 'flex', flexDirection: 'column', gap: '10px' }}> {notifications.map(notification => ( <Notification key={notification.id} message={notification.message} close={() => handleClose(notification.id)} /> ))} </div> ); }
3. 在应用顶层挂载容器
在index.js或App.js的最外层挂载容器,确保它能全局渲染通知:
import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import NotificationContainer from './NotificationContainer'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <NotificationContainer /> <App /> </React.StrictMode> );
4. 任意位置调用工具函数
现在可以在React组件、redux actions、sagas甚至普通工具函数中直接调用:
// 在redux action中调用 export const fetchData = () => async (dispatch) => { try { const data = await api.fetchData(); dispatch({ type: 'FETCH_SUCCESS', payload: data }); showNotification('数据获取成功!'); } catch (error) { dispatch({ type: 'FETCH_ERROR', payload: error.message }); showNotification('数据获取失败,请重试', () => { console.log('用户关闭了通知'); }); } }; // 在saga中调用 function* fetchDataSaga() { try { const data = yield call(api.fetchData); yield put({ type: 'FETCH_SUCCESS', payload: data }); showNotification('数据获取成功!'); } catch (error) { yield put({ type: 'FETCH_ERROR', payload: error.message }); showNotification('数据获取失败,请重试'); } } // 在普通函数中调用 function handleFormSubmit() { showNotification('表单提交成功'); }
关键逻辑说明
- 全局队列:作为非React代码和React组件的中间层,外部调用工具函数时将通知信息加入队列。
- 顶层容器:监听队列变化,异步处理每个通知,负责渲染、自动关闭和手动关闭逻辑。
- Promise与resolveRef:用Promise控制通知显示时长,resolveRef存储每个通知的resolve函数,实现手动关闭时提前结束Promise并移除通知。
内容的提问来源于stack exchange,提问作者marcus-linmarkson
相关产品推荐
相关产品推荐

