React hook实现简单的websocket封装方式

目录
  • React hook实现websocket封装
  • react自定义hook解决websocket连接,useWebSocket
    • 1、描述
    • 2、代码

React hook实现websocket封装

新建websocket.ts文件

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();

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • React前端DOM常见Hook封装示例上

    目录 引言 useEventListener useClickAway useEventTarget useTitle useFavicon 引言 本文是深入浅出 ahooks 源码系列文章的第十四篇,这个系列的目标主要有以下几点: 加深对 React hooks 的理解. 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库. 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择. 上一篇我们探讨了 ahooks 对 DOM 类 Hooks 使用规范,以及源码中是

  • React前端DOM常见Hook封装示例下

    目录 引言 useFullscreen useHover useDocumentVisibility 引言 本文是深入浅出 ahooks 源码系列文章的第十五篇,这个系列的目标主要有以下几点: 加深对 React hooks 的理解. 学习如何抽象自定义 hooks.构建属于自己的 React hooks 工具库. 培养阅读学习源码的习惯,工具库是一个对源码阅读不错的选择. 上文指路:React前端DOM常见Hook封装示例上 本篇接着针对关于 DOM 的各个 Hook 封装进行解读. useF

  • 详解如何构建自己的react hooks

    1. 常用的一个 hooks 官方中提供了几个内置的钩子,我们简单了解下他们的用法. 1.1 useState: 状态钩子 需要更新页面状态的数据,我们可以把他放到 useState 的钩子里.例如点击按钮一下,数据加 1 的操作: const [count, setCount] = useState(0); return (<> <p>{ count}</p> <button onClick = { () => setCount(count + 1) }&

  • React hook实现简单的websocket封装方式

    目录 React hook实现websocket封装 react自定义hook解决websocket连接,useWebSocket 1.描述 2.代码 React hook实现websocket封装 新建websocket.ts文件 import {useState, useRef, useEffect} from 'react' const useWebsocket = ({ url:string, verify }) => {     const ws = useRef<WebSocket

  • React Hook 父子组件相互调用函数方式

    目录 React Hook 父子组件相互调用函数 1.子组件调用父组件函数方法 2.父组件调用子组件函数方法 React Hook 父子组件传值 父组件 子组件 React Hook 父子组件相互调用函数 1.子组件调用父组件函数方法 //父组件 let Father=()=>{     let getInfo=()=>{              }     return ()=>{         <div>             <Children       

  • 利用 Go 语言编写一个简单的 WebSocket 推送服务

    本文中代码可以在 github.com/alfred-zhong/wserver获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息都是通过短信,微信和 App 推送给用户的,现在要让登录用户在网页端也能实时接收到报警推送. 依稀记得以前工作的时候遇到过类似的需求.因为以前的浏览器标准比较陈旧,并且那时用 Java 较多,所以那时候解决这个问题就用了 Comet4J.具体的原理就是长轮询,长链接.但现在毕竟 html5 流行开来了,IE 都被 Edge 接替了,再用以前这种技术就显得过

  • React Hook的使用示例

    这篇文章分享两个使用React Hook以及函数式组件开发的简单示例. 一个简单的组件案例 Button组件应该算是最简单的常用基础组件了吧.我们开发组件的时候期望它的基础样式能有一定程度的变化,这样就可以适用于不同场景了.第二点是我在之前做项目的时候写一个函数组件,但这个函数组件会写的很死板,也就是上面没有办法再绑定基本方法.即我只能写入我已有的方法,或者特性.希望编写Button组件,即使没有写onClick方法,我也希望能够使用那些自带的默认基本方法. 对于第一点,我们针对不同的class

  • React hook超详细教程

    目录 什么是hook useState useEffect useRef useCallback useMemo useContext useReducer 什么是hook React Hook是React 16.8版本之后添加的新属性,用最简单的话来说,React Hook就是一些React提供的内置函数,这些函数可以让函数组件和类组件一样能够拥有组件状态(state)以及进行副作用(side effect) 但是不要什么业务都使用hook,请在合适的时候使用hook,否则会造成性能问题.(能

  • React Hook用法示例详解(6个常见hook)

    1.useState:让函数式组件拥有状态 用法示例: // 计数器 import { useState } from 'react' const Test = () => { const [count, setCount] = useState(0); return ( <> <h1>点击了{count}次</h1> <button onClick={() => setCount(count + 1)}>+1</button> &l

  • react+ts实现简单jira项目的最佳实践记录

    练手的一套项目 react+ts 虽然内容较少,但是干货挺多,尤其是对hooks的封装,ts的泛型的理解,使用更上一层楼 项目代码:https://gitee.com/fine509/react_jiar 效果图 这是三个主要页面,还有一些小细节 等等 一些值得注意的地方(只是讲大概的功能,没有具体的详解怎么用) 使用错误边界处理,getDerivedStateFromError来处理当某个页面某处地方有报错的时候显示报错组件而不是挂掉. useSearchParams的使用 这个api可以获取

  • 在koa中简单使用Websocket连接的方法示例

    目录 前言 ws模块安装 websocket初始化 websocket下发数据 总结 前言 在一次项目需求会上,有个新需求是要让用户从管理后台主动下发数据到app前端,从而让前端那边对这主动下发的数据做一些用户交互.实现思路很清晰,用Websocket的方式.Websocket 是一种自然的全双工.双向.单套接字连接,是建立在 TCP 协议上的. 相比于 HTTP 协议,Websocket 链接一旦建立,即可进行双向的实时通信: ws模块安装 由于后台是基于node+koa2+mongo进行开发

  • React Hook中useState更新延迟问题及解决

    目录 React Hook中useState更新延迟 React Hook useState连续更新对象问题 React Hook中useState更新延迟 方法一:去掉useEffect的第二个参数 例如以下代码 错误实例 const[zoom, setZoom] = useState(0); useEffect(() = >{     document.getElementById('workspace-content').addEventListener('mousewheel', scr

  • React Hook中的useEffecfa函数的使用小结

    目录 useEffect的详细解析 useEffecf基本使用 清除副作用(Effect) 使用多个useEffect useEffect性能优化 useEffect的详细解析 useEffecf基本使用 书接上文, 上一篇文章我们讲解了State Hook, 我们已经可以通过这个hook在函数式组件中定义state 我们知道在类组件中是可以有生命周期函数的, 那么如何在函数组件中定义类似于生命周期这些函数呢? Effect Hook 可以让你来完成一些类似于class中生命周期的功能; 事实上

随机推荐