React?hook實(shí)現(xiàn)簡單的websocket封裝方式
React hook實(shí)現(xiàn)websocket封裝
新建websocket.ts文件
import {useState, useRef, useEffect} from 'react'
const useWebsocket = ({ url:string, verify }) => {
? ? const ws = useRef<WebSocket | null>(null)
? ? // socket 數(shù)據(jù)
? ? const [wsData, setMessage] = useState({})
? ? // ?socket 狀態(tài)
? ? const [readyState, setReadyState] = useState<any>({ key: 0, value: '正在連接中' })
? ? const creatWebSocket = () => {
? ? ? ? const stateArr = [
? ? ? ? ? ? {key: 0, value: '正在連接中'},
? ? ? ? ? ? {key: 1, value: '已經(jīng)連接并且可以通訊'},
? ? ? ? ? ? {key: 2, value: '連接正在關(guān)閉'},
? ? ? ? ? ? {key: 3, value: '連接已關(guān)閉或者沒有連接成功'},
? ? ? ? ]
? ? ? ? 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()
? ? ? ? }
? ? }
? ? // ?關(guān)閉 WebSocket
? ? const closeWebSocket = () => {
? ? ? ? ws.current?.close()
? ? }
? ? // 發(fā)送數(shù)據(jù)
? ? 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這里一共暴露出四個參數(shù)。分別是
wsData(獲得的 socket 數(shù)據(jù))readyState(當(dāng)前 socket 狀態(tài))closeWebSocket(關(guān)閉 socket)reconnect(重連)
通過這幾個簡單的參數(shù)能夠覆蓋一般場景的需要。其中 verify 參數(shù)是控制是否有權(quán)限進(jìn)行請求。可以根據(jù) 實(shí)際需求進(jìn)行刪除或新增。
重連啥的通過監(jiān)聽 readyState 狀態(tài)進(jìn)行相應(yīng)操作。
下面代碼為使用方法:
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:端口', // 此參數(shù)為websocket地址
? ? ?? ?verify // 此參數(shù)控制是否有權(quán)限,請求該方法
??? ? })
?? ?useEffect(() => {
? ? ?? ?// 不在白名單人員之間不執(zhí)行后續(xù)操作,不需要可以刪除
? ? ?? ?if (!verify) {
? ? ? ?? ??? ?return
?? ? ? ?}
?? ? ? ?
?? ? ? ?// 接受到socket數(shù)據(jù), 進(jìn)行業(yè)務(wù)邏輯處理
?? ? ? ?if (Object.keys(wsData).length !== 0) {
?? ? ? ??? ?console.log(wsData)
?? ? ? ?}
?? ? ? ?
?? ? ? ?// 如果是已關(guān)閉且是當(dāng)前頁面自動重連
?? ? ? ?if (readyState.key === 3 && isLocalPage) {
?? ? ? ? ?reconnect()
?? ? ? ?}
?? ? ? ?// 不是當(dāng)前頁面 清空 webSocket 此處為優(yōu)化代碼使用的,不需要可以直接刪除。
?? ? ? ?if (!isLocalPage) {
?? ? ? ? ?closeWebSocket()
?? ? ? ?}
? ?? ?}, [wsData, readyState, isLocalPage, verify])
? }對于 isLocalPage 感興趣可以看下面代碼是判斷用戶是否在當(dāng)前頁面。 此方法可以放在useEffect。
/*
?** 判斷用戶是否離開當(dāng)前頁面,離開后不請求輪詢接口,回到當(dāng)前頁面重新執(zhí)行輪詢
?*/
useEffect(() => {
? ? ? document.addEventListener('visibilitychange', function () {
? ? ? ? ? // 頁面變?yōu)椴豢梢姇r觸發(fā)
? ? ? ? ? if (document.visibilityState === 'hidden') {
? ? ? ? ? ? ? setIsLocalPage(false)
? ? ? ? ? }
? ? ? ? ? // 頁面變?yōu)榭梢姇r觸發(fā)
? ? ? ? ? if (document.visibilityState === 'visible') {
? ? ? ? ? ? ? setIsLocalPage(true)
? ? ? ? ? }
? ? ? })
? })最后,在這個代碼中沒有涉及到的場景就是 心跳機(jī)制,一般簡單的需求可以不考慮,這塊邏輯實(shí)現(xiàn)上也比較簡單,這里就不多加闡述了。
react自定義hook解決websocket連接,useWebSocket
react自定義hook,useWebSocket
1、描述
本來項(xiàng)目的告警和消息提醒是用的接口30秒調(diào)用一次,這次要改成webSocket傳輸。
因?yàn)榍岸耸怯玫膆ttps,后端用的http,后端的socket只支持ws不支持wss,這里使用了webpack-dev-server的proxy代理了一下。
target:ws目標(biāo)地址、pathRewrite:地址重寫,這里是把/aapp_socket重寫成aapp/websocket,ws:是否開啟socket,secure: 默認(rèn)情況下不接收轉(zhuǎn)發(fā)到https的服務(wù)器上,如果希望支持,可以設(shè)置為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`;
實(shí)際的訪問的地址就是:
`ws://xxx.xxx.xxx/aapp/websocket
2、代碼
這里socket,沒有配置心跳監(jiān)測,還是通過我主動去推送來獲取信息。這里是獲取告警數(shù)和消息數(shù)量,
首先綁定websocket的事件。主要就是在message的事件中,連接成功后端返回的是sucess,就不做操作。后面就是判斷返回的消息格式是否正確,如果不正確就重新連接。
還可以把獲取消息的時間間隔,和重新連接間隔,地址等變量抽出來,作為參數(shù)傳進(jìn)來。
import {useCallback, useRef, useState, useEffect} from 'react';
const token = window.localStorage.getItem('authorization');
const userId = JSON.parse(window.localStorage.getItem('userInfo') || '')?.id;
// 獲取告警數(shù)量
const UNREAD_WARN_COUNT = 'UNREAD_WARN_COUNT';
// 獲取消息數(shù)量
const UNREAD_MSG_COUNT = 'UNREAD_MSG_COUNT';
// 獲取消息的間隔
const INT_TIME = 5000;
// websocket狀態(tài)
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);
? ? // 開啟事件,主動獲取數(shù)據(jù)
? ? const socketOnOpen = useCallback(() => {
? ? ? ? // 判斷連接狀態(tài)是不是open
? ? ? ? if (socket?.current?.readyState === webSocketStatus.OPEN) {
? ? ? ? ? ? // 第一次加載觸發(fā)一次
? ? ? ? ? ? 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]);
? ? // 關(guān)閉事件重新連接
? ? const socketOnClose = useCallback(() => {
? ? ? ? setReset(true);
? ? }, []);
? ? // 出錯事件
? ? const socketOnError = useCallback((err: any) => {
? ? ? ? console.log('err: ', err);
? ? }, []);
? ? // 收發(fā)信息
? ? 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();
以上為個人經(jīng)驗(yàn),希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
react ant-design Select組件下拉框map不顯示的解決
這篇文章主要介紹了react ant-design Select組件下拉框map不顯示的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03
React?正確使用useCallback?useMemo的方式
這篇文章主要介紹了React?正確使用useCallback?useMemo的方式,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價值,需要的朋友可以參考一下2022-08-08
使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽
這篇文章主要介紹了使用react-native-image-viewer實(shí)現(xiàn)大圖預(yù)覽,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
在React項(xiàng)目中使用iframe嵌入一個網(wǎng)站的步驟
本文介紹了如何在React項(xiàng)目中通過iframe嵌入百度網(wǎng)站的步驟,首先創(chuàng)建一個Baidu.js組件,并在該組件中設(shè)置iframe來加載百度,然后在App.js中引入并使用Baidu組件,還討論了因安全策略可能無法加載某些網(wǎng)站的問題,需要的朋友可以參考下2024-09-09

