日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React?hook實現簡單的websocket封裝方式_React

作者:bigHead- ? 更新時間: 2022-11-06 編程語言

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

欄目分類
最近更新