React集成SignalR最佳实践:封装SignalR为自定义Hook方案
解决方案:将SignalR逻辑封装到自定义Hook
自定义Hook实现
我们可以创建独立的useSignalRImageData Hook,把数据获取、SignalR连接管理、通知响应逻辑全部封装进去,让组件只专注于UI渲染。
import { useReducer, useEffect, useCallback } from "react"; import { cardDataReducer, initialCapturedImages } from "../../reducers/reducer"; import { GetImages } from "../../services/mongo"; import { setUpListener } from "../../services/signalR"; export function useSignalRImageData(limit = 10) { const [cardData, dispatch] = useReducer(cardDataReducer, initialCapturedImages); // 封装数据获取逻辑,用useCallback避免重复创建函数 const fetchData = useCallback(async () => { try { const data = await GetImages(limit); dispatch({ type: 'fetched', capturedImageFetch: data, id: "", text: "", capturedImage: undefined }); } catch (err) { console.error("获取图片失败:", err); } }, [limit]); useEffect(() => { // 初始化加载数据 fetchData(); // 建立SignalR监听,收到通知时触发数据刷新 const signalRConnection = setUpListener(() => { fetchData(); }); // 组件卸载时清理SignalR连接,避免内存泄漏 return () => { // 根据你的setUpListener返回的连接对象调整清理逻辑,比如调用stop方法 if (signalRConnection?.stop) { signalRConnection.stop(); } }; }, [fetchData]); return cardData; }
Hook逻辑说明
- 状态复用:保留原有的
useReducer状态管理逻辑,保证状态一致性 - 初始化加载:组件挂载时自动执行一次数据获取
- SignalR管理:在同一个
useEffect中创建监听,仅收到服务器通知时触发数据刷新 - 自动清理:组件卸载时断开SignalR连接,防止无效连接残留
- 性能优化:用
useCallback包裹fetchData,避免因函数引用变化导致的重复执行
重构原组件
简化BaseMediaLayout组件,直接使用自定义Hook获取数据,组件仅负责UI渲染:
import * as React from "react"; import Grid from '@mui/material/Unstable_Grid2'; // Grid version 2 import { Container } from "@mui/material"; import { useSignalRImageData } from "./useSignalRImageData"; // 调整Hook导入路径 export function BaseMediaLayout() { const cardData = useSignalRImageData(10); return ( <Container> <Grid> {/* 这里渲染你的图片卡片,例如:cardData.items.map(item => ...) */} </Grid> </Container> ); }
解决的核心问题
- 消除重复API调用:仅在初始化和收到SignalR通知时调用
GetImages,避免原代码中依赖lastUpdated导致的不必要重复请求 - 逻辑解耦:SignalR和数据获取逻辑从组件中剥离,组件更简洁,维护性更强
- 避免连接泄漏:自动处理SignalR连接的创建与销毁,防止组件重复渲染时创建多个连接
- 提升可复用性:自定义Hook可以在其他需要监听图片更新的组件中直接复用
内容的提问来源于stack exchange,提问作者user1730289
相关产品推荐
相关产品推荐

