網站首頁 編程語言 正文
React hook實現websocket封裝
新建websocket.ts文件,websocket在線測試
import {useState, useRef, useEffect} from 'react' const useWebsocket = ({ url:string, verify }) => { const ws = useRef<WebSocket | null>(null) // socket 數據 const [wsData, setMessage] = useState({}) // socket 狀態 const [readyState, setReadyState] = useState<any>({ key: 0, value: '正在連接中' }) const creatWebSocket = () => { const stateArr = [ {key: 0, value: '正在連接中'}, {key: 1, value: '已經連接并且可以通訊'}, {key: 2, value: '連接正在關閉'}, {key: 3, value: '連接已關閉或者沒有連接成功'}, ] try { ws.current = new WebSocket(url) ws.current.onopen = () => { setReadyState(stateArr[ws.current?.readyState ?? 0]) } ws.current.onclose = () => { setReadyState(stateArr[ws.current?.readyState ?? 0]) } ws.current.onerror = () => { setReadyState(stateArr[ws.current?.readyState ?? 0]) } ws.current.onmessage = (e) => { setMessage({...JSON.parse(e.data)}) } } catch (error) { console.log(error) } } const webSocketInit = () => { if (!ws.current || ws.current.readyState === 3) { creatWebSocket() } } // 關閉 WebSocket const closeWebSocket = () => { ws.current?.close() } // 發送數據 const sendMessage = (str:string) => { ws.current?.send(str) } //重連 const reconnect = () => { try { closeWebSocket() ws.current = null creatWebSocket() } catch (e) { console.log(e) } } useEffect(() => { verify && webSocketInit() return () => { ws.current?.close() } }, [ws,verify]) return { wsData, readyState, closeWebSocket, reconnect, sendMessage, } } export default useWebsocket
這里一共暴露出四個參數。分別是
-
wsData
(獲得的 socket 數據) -
readyState
(當前 socket 狀態) -
closeWebSocket
(關閉 socket) -
reconnect
(重連)
通過這幾個簡單的參數能夠覆蓋一般場景的需要。其中 verify 參數是控制是否有權限進行請求。可以根據 實際需求進行刪除或新增。
重連啥的通過監聽 readyState 狀態進行相應操作。
下面代碼為使用方法:
import React, { useState, useEffect } from 'react' import useWebsocket from '../../tools/webSocket' export default function () { const [isLocalPage, setIsLocalPage] = useState(true) const { wsData, readyState, closeWebSocket, reconnect } = useWebsocket({ url: 'ws://ip:端口', // 此參數為websocket地址 verify // 此參數控制是否有權限,請求該方法 }) useEffect(() => { // 不在白名單人員之間不執行后續操作,不需要可以刪除 if (!verify) { return } // 接受到socket數據, 進行業務邏輯處理 if (Object.keys(wsData).length !== 0) { console.log(wsData) } // 如果是已關閉且是當前頁面自動重連 if (readyState.key === 3 && isLocalPage) { reconnect() } // 不是當前頁面 清空 webSocket 此處為優化代碼使用的,不需要可以直接刪除。 if (!isLocalPage) { closeWebSocket() } }, [wsData, readyState, isLocalPage, verify]) }
對于 isLocalPage 感興趣可以看下面代碼是判斷用戶是否在當前頁面。 此方法可以放在useEffect。
/* ** 判斷用戶是否離開當前頁面,離開后不請求輪詢接口,回到當前頁面重新執行輪詢 */ useEffect(() => { document.addEventListener('visibilitychange', function () { // 頁面變為不可見時觸發 if (document.visibilityState === 'hidden') { setIsLocalPage(false) } // 頁面變為可見時觸發 if (document.visibilityState === 'visible') { setIsLocalPage(true) } }) })
最后,在這個代碼中沒有涉及到的場景就是 心跳機制,一般簡單的需求可以不考慮,這塊邏輯實現上也比較簡單,這里就不多加闡述了。
react自定義hook解決websocket連接,useWebSocket
react自定義hook,useWebSocket
1、描述
本來項目的告警和消息提醒是用的接口30秒調用一次,這次要改成webSocket傳輸。
因為前端是用的https,后端用的http,后端的socket只支持ws不支持wss,這里使用了webpack-dev-server的proxy代理了一下。
target:ws目標地址、pathRewrite:地址重寫,這里是把/aapp_socket重寫成aapp/websocket,ws:是否開啟socket,secure: 默認情況下不接收轉發到https的服務器上,如果希望支持,可以設置為false ,changeOrigin:是否跨域。差不多就這個意思
'/aapp_socket': { target: `ws://xxx.xxx.xxx/`, pathRewrite: { '^/aapp_socket': 'aapp/websocket', }, ws: true, secure: false, changeOrigin: true, },
使用連接的地址:
`wss://localhost:3000/aapp_socket`;
實際的訪問的地址就是:
`ws://xxx.xxx.xxx/aapp/websocket
2、代碼
這里socket,沒有配置心跳監測,還是通過我主動去推送來獲取信息。這里是獲取告警數和消息數量,
首先綁定websocket的事件。主要就是在message的事件中,連接成功后端返回的是sucess,就不做操作。后面就是判斷返回的消息格式是否正確,如果不正確就重新連接。
還可以把獲取消息的時間間隔,和重新連接間隔,地址等變量抽出來,作為參數傳進來。
import {useCallback, useRef, useState, useEffect} from 'react'; const token = window.localStorage.getItem('authorization'); const userId = JSON.parse(window.localStorage.getItem('userInfo') || '')?.id; // 獲取告警數量 const UNREAD_WARN_COUNT = 'UNREAD_WARN_COUNT'; // 獲取消息數量 const UNREAD_MSG_COUNT = 'UNREAD_MSG_COUNT'; // 獲取消息的間隔 const INT_TIME = 5000; // websocket狀態 const webSocketStatus = { CONNECTING: 0, OPEN: 1, CLOSING: 2, CLOSED: 3, }; const useWebSocket = () => { const [reset, setReset] = useState<boolean>(false); const socket = useRef<WebSocket>(); const sendCount = useRef<number>(1); const [alarmCount, setAlarmCount] = useState<number>(0); const [messageCount, setMessageCount] = useState<number>(0); // 開啟事件,主動獲取數據 const socketOnOpen = useCallback(() => { // 判斷連接狀態是不是open if (socket?.current?.readyState === webSocketStatus.OPEN) { // 第一次加載觸發一次 socket?.current?.send(JSON.stringify({businessKey: [UNREAD_MSG_COUNT, UNREAD_WARN_COUNT]})); } const timer = setInterval(() => { if (socket?.current?.readyState === webSocketStatus.OPEN) { socket?.current?.send(JSON.stringify({businessKey: [UNREAD_MSG_COUNT, UNREAD_WARN_COUNT]})); } }, INT_TIME); // 返回信息出錯清除定時器 if (sendCount.current === 0) { clearInterval(timer); setReset(true); } }, [sendCount]); // 關閉事件重新連接 const socketOnClose = useCallback(() => { setReset(true); }, []); // 出錯事件 const socketOnError = useCallback((err: any) => { console.log('err: ', err); }, []); // 收發信息 const socketOnMessage = useCallback( (e: any) => { if (e.data === 'success') return; const alarmCountObj = JSON.parse(e.data); const paramNameArr = Object.keys(alarmCountObj); // 判斷返回告警保持連接否則斷開連接 if (paramNameArr[1] === 'UNREAD_WARN_COUNT') { sendCount.current += 1; setAlarmCount(alarmCountObj.UNREAD_WARN_COUNT); setMessageCount(alarmCountObj.UNREAD_MSG_COUNT); } else { sendCount.current = 0; } }, [sendCount], ); // 初始化連接socket const socketInit = useCallback(() => { try { const scoketUrl = `wss://${window.location.host}/aapp_socket/${userId}/${token}`; const socketObj = new WebSocket(scoketUrl); socketObj.addEventListener('close', socketOnClose); socketObj.addEventListener('error', socketOnError); socketObj.addEventListener('message', socketOnMessage); socketObj.addEventListener('open', socketOnOpen); socket.current = socketObj; sendCount.current = 1; } catch (err) { console.log('err: ', err); } }, [socketOnClose, socketOnError, socketOnMessage, socketOnOpen]); // 初始化連接socket useEffect(() => { socketInit(); }, [socketInit]); // 斷線重連 useEffect(() => { if (!reset) return; setTimeout(() => { socketInit(); setReset(false); }, 30000); }, [reset, socketInit]); return [alarmCount, messageCount]; }; export default useWebSocket;
使用
// 告警socket連接 const [alarmCount, messageCount] = useWebSocket();
原文鏈接:https://blog.csdn.net/weixin_47872719/article/details/123702625
相關推薦
- 2022-03-28 Python中三種條件語句示例介紹_python
- 2022-11-20 Python利用pangu模塊實現文本格式化小工具_python
- 2023-05-23 Golang拾遺之實現一個不可復制類型詳解_Golang
- 2022-07-07 Python?pluggy框架使用示例代碼_python
- 2022-11-30 Golang迭代如何在Go中循環數據結構使用詳解_Golang
- 2023-03-23 Android進階NestedScroll嵌套滑動機制實現吸頂效果詳解_Android
- 2022-08-20 PostgreSQL實現按年、月、日、周、時、分、秒的分組統計_PostgreSQL
- 2022-07-27 Python?pyecharts?Line折線圖的具體實現_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支