使用React 18和WebSocket构建实时通信功能
1. 引言
WebSocket是一种在Web应用中实现双向通信的协议。它允许服务器主动向客户端推送数据,而不需要客户端发起请求。在现代的实时应用中,WebSocket经常用于实时数据传输、聊天功能、实时通知和多人协作等场景。在本篇博客中,我们将探索如何在React 18应用中使用WebSocket来实现实时通信。
2. 准备工作
在开始之前,我们需要安装React 18,并确定你已经掌握了React Hooks的基本知识。此外,我们还将使用WebSocket的npm包来实现WebSocket连接。你可以通过以下命令使用npm或yarn来安装它:
npm install websocket
# 或
yarn add websocket
3. 编写自定义钩子
import { useEffect, useRef, useState } from 'react';
import WebSocketClient from 'websocket';export function useWebSocket(accessToken: string, requestName: string): SocketType {const clientRef = useRef<WebSocketClient | null>(null);const [isActive, setIsActive] = useState<boolean>(false);const [socketClient, setSockClient] = useState<WebSocketClient | null>(null);// 获取urllet port = window.location.port;let wsUrl = '';if (window.location.protocol === 'https:') {//如果当前是HTTPS加密的,那么使用wssif (!port) {port = '4174';}wsUrl = 'wss:';} else {if (!port) {port = '8080';}wsUrl = 'ws:';}wsUrl +=`//${window.location.hostname}:${port}/api/ws/plugins/${requestName}?token=` +accessToken;if (!socketClient) {setSockClient(new WebSocketClient(wsUrl, isActive)); // 创建 WebSocketClient 实例并传入 URL 和活动状态 isActive}useEffect(() => {clientRef.current = socketClient;if (!socketClient?.socket) {socketClient?.start(); // 启动WebSocket连接}return () => {socketClient?.close(); // 组件卸载时关闭WebSocket连接};}, []);// 建立 WebSocket 连接const connect = () => {const client = clientRef.current;if (client) {client.connect(); // 建立WebSocket连接}};// 关闭 WebSocket 连接const close = () => {const client = clientRef.current;if (client) {client.close(); // 关闭WebSocket连接}};// 订阅消息处理程序const subscribe = (handler: MessageHandler) => {const client = clientRef.current;setIsActive(true);if (client) {client.subscribe(handler);}};// 取消订阅消息const unsubscribe = () => {const client = clientRef.current;if (client && isActive) {setIsActive(false);client.unsubscribe();}};// 发送消息const send = (message: string) => {const client = clientRef.current;if (client && client.socket?.readyState === WebSocket.OPEN) {client.send(message); // 发送消息} else if (client && client.socket?.readyState === WebSocket.CLOSED) {// WebSocket连接未建立或已关闭,需要重新建立连接client.connect(); // 建立WebSocket连接}};return { connect, close, subscribe, unsubscribe, send };
}
在上述代码中,我们使用useRef来保存WebSocketClient实例,使用useState来管理isActive和socketClient状态。通过创建WebSocket连接的URL和accessToken,我们可以在useEffect钩子中实例化WebSocketClient。然后使用useEffect钩子来启动和关闭WebSocket连接,并在组件卸载时关闭连接。
4. 创建WebSocketProvider
为了在整个应用中共享WebSocket连接对象,我们需要创建一个WebSocketProvider组件。这个组件将使用提供者模式将连接对象提供给子组件。
在你的项目中创建一个名为WebSocketProvider.js的文件,并添加以下代码:
import React, { useContext, useEffect } from 'react';
import { useWebSocket } from './useWebSocket';const WebSocketContext = React.createContext();export const useWebSocketContext = () => {return useContext(WebSocketContext);
};export const WebSocketProvider = ({ children, accessToken }) => {const webSocket = useWebSocket(accessToken);useEffect(() => {webSocket.connect();return () => {webSocket.close();};}, [webSocket]);return (<WebSocketContext.Provider value={webSocket}>{children}</WebSocketContext.Provider>);
};
在上述代码中,我们使用useWebSocket钩子来获取WebSocket连接对象,并在useEffect钩子中连接WebSocket,并在组件卸载时关闭连接。然后,我们将连接对象提供给子组件,通过创建一个WebSocketContext.Provider。
5. 在组件中使用共享连接
现在,我们可以在应用的任何组件中使用共享的WebSocket连接了。
假设我们有一个名为ChatComponent的组件,它需要使用WebSocket连接来实现实时聊天功能。在ChatComponent.js文件中,添加以下代码:
import React from 'react';
import { useWebSocketContext } from './WebSocketProvider';function ChatComponent() {const webSocket = useWebSocketContext();const sendMessage = () => {if (webSocket) {webSocket.send('Hello, WebSocket!');}};return (<div><button onClick={sendMessage}>Send Message</button></div>);
}export default ChatComponent;
在上述代码中,我们使用useWebSocketContext来获取WebSocket连接对象。然后,我们可以在组件中调用send方法来发送消息。
6. 示例应用:实时聊天
让我们使用上述代码,创建一个实时聊天应用作为示例。在你的项目中,创建一个名为RealTimeChatApp的文件夹,然后在文件夹中创建以下文件:
`RealTimeChatApp.js:` 主应用组件
`ChatComponent.js:` 实时聊天组件
`WebSocketProvider.js`: `WebSocket`连接提供者
在RealTimeChatApp.js中,添加以下代码:
import React from 'react';
import ChatComponent from './ChatComponent';
import { WebSocketProvider } from './WebSocketProvider';function RealTimeChatApp() {const accessToken = 'your_access_token'; // 替换为你的访问令牌return (<WebSocketProvider accessToken={accessToken}><div><h1>Real Time Chat App</h1><ChatComponent /></div></WebSocketProvider>);
}export default RealTimeChatApp;
然后,在ChatComponent.js中,添加以下代码:
import React from 'react';
import { useWebSocketContext } from './WebSocketProvider';
function ChatComponent() {const webSocket = useWebSocketContext();const [messages, setMessages] = React.useState([]);const [newMessage, setNewMessage] = React.useState('');React.useEffect(() => {const messageHandler = (message) => {setMessages((prevMessages) => [...prevMessages, message]);};webSocket.subscribe(messageHandler);return () => {webSocket.unsubscribe();};}, [webSocket]);const sendMessage = () => {if (webSocket) {webSocket.send(newMessage);setNewMessage('');}};return (<div><div>{messages.map((message, index) => (<div key={index}>{message}</div>))}</div><div><inputtype="text"value={newMessage}onChange={(e) => setNewMessage(e.target.value)}/><button onClick={sendMessage}>Send</button></div></div>);
}export default ChatComponent;
最后,启动你的应用,访问RealTimeChatApp组件,即可在浏览器中查看实时聊天功能。
7. 总结
本文介绍了如何在React 18应用中使用WebSocket来实现实时通信,并展示了如何通过自定义钩子和提供者模式来共享WebSocket连接对象。通过这种方式,我们可以在多个组件中使用同一个连接对象,从而避免了不必要的连接重复实例化和性能开销。WebSocket在现代实时应用中发挥着重要作用,帮助我们实现更高效的通信和用户体验。
希望本文能够帮助你理解如何在React 18中使用WebSocket,并在应用中实现共享连接的目标。如果你想进一步探索WebSocket的高级用法,可以深入了解WebSocket的各种选项和特性,以满足你的实际需求。
致谢
感谢您阅读本文,希望本文对你有所帮助。特别感谢React 18和WebSocket社区的开发者们,为我们提供了强大的工具和技术,让实时通信变得更加简单和高效。
相关文章:
使用React 18和WebSocket构建实时通信功能
1. 引言 WebSocket是一种在Web应用中实现双向通信的协议。它允许服务器主动向客户端推送数据,而不需要客户端发起请求。在现代的实时应用中,WebSocket经常用于实时数据传输、聊天功能、实时通知和多人协作等场景。在本篇博客中,我们将探索如…...
vue3使用vue-router嵌套路由(多级路由)
文章目录 1、Vue3 嵌套路由2、项目结构3、编写相关页面代码3.1、编写route文件下 index.ts文件3.2、main.ts文件代码:3.3、App.vue文件代码:3.4、views文件夹下的Home文件夹下的index.vue文件代码:3.5、views文件夹下的Home文件夹下的Tigerhh…...
openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表
文章目录 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-处理错误表164.1 操作场景164.2 查询错误信息164.3 处理数据导入错误 openGauss学习笔记-164 openGauss 数据库运维-备份与恢复-导入数据-使用COPY FROM STDIN导入数据-…...
QT Widget - 随便画个圆
简介 实现在界面中画一个圆, 其实目的是想画一个LED效果的圆。代码 #include <QApplication> #include <QWidget> #include <QPainter> #include <QColor> #include <QPen>class LEDWidget : public QWidget { public:LEDWidget(QWidget *pare…...
js输入框部分内容不可编辑,其余正常输入,el-input和el-select输入框和多个下拉框联动后的内容不可修改
<tr>//格式// required自定义指令<e-td :required"!read" label><span>地区:</span></e-td><td>//v-if"!read && this.data.nationCode 148"显示逻辑<divclass"table-cell-flex"sty…...
分布式文件存储系统minio了解下
什么是minio minio 是一个基于 Apache License v2.0 开源协议的对象存储服务。非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小。 是一种海量、安全、低成本、高可靠的云存储…...
迅为RK3568开发板使用OpenCV处理图像-ROI区域-位置提取ROI
在图像处理过程中,我们可能会对图像的某一个特定区域感兴趣,该区域被称为感兴趣区域(Region of Interest, ROI)。在设定感兴趣区域 ROI 后,就可以对该区域进行整体操作。 位置提取 ROI 本小节代码在配套资料“iTOP-3…...
重新认识Word——尾注
重新认识Word——尾注 参考文献格式文献自动生成器插入尾注将数字带上方括号将参考文献中的标号改为非上标 多处引用一篇文献多篇文献被一处引用插入尾注有横线怎么删除?删除尾注 前面我们学习了如何给图片,公式自动添加编号,今天我们来看看毕…...
所有学前教育专业,一定要刷到这篇啊
我是真的希望所有学前教育的宝子都能刷到这篇啊啊,只要输入需求,几秒它就给你写出来了,而且不满意还可以重新写多,每次都是不一样的内容。重复率真的不高,需求越多,生成的文字内容越精准!&#…...
colmap三维重建核心逻辑梳理
colmap三维重建核心逻辑梳理 1. 算法流程束流2. 初始化3. 重建主流程 1. 算法流程束流 重建核心逻辑见 incremental_mapper.cc 中 IncrementMapperController 中 Reconstruct 初始化变量和对象判断是否有初始重建模型,若有,则获取初始重建模型数量&am…...
查询某个类是在哪个JAR的什么版本开始出现的方法
背景 我们在依赖第三方JAR时,同时也会间接的依赖第三方JAR引用的依赖,而当我们项目中某个依赖的版本与第三方JAR依赖的版本不一致时,可能会导致第三方JAR的在运行时无法找到某些方法或类,从而无法正常使用。 如我正在开发的一个…...
Linux本地搭建StackEdit Markdown编辑器结合内网穿透实现远程访问
文章目录 1. docker部署Stackedit2. 本地访问3. Linux 安装cpolar4. 配置Stackedit公网访问地址5. 公网远程访问Stackedit6. 固定Stackedit公网地址 StackEdit是一个受欢迎的Markdown编辑器,在GitHub上拥有20.7k Star!,它支持将Markdown笔记保…...
k8s中ConfigMap、Secret创建使用演示、配置文件存储介绍
目录 一.ConfigMap(cm) 1.适用场景 2.创建并验证configmap (1)以yaml配置文件创建configmap,验证变化是是否同步 (2)--from-file以目录或文件 3.如何使用configmap (1&#x…...
Linux服务器性能优化小结
文章目录 生产环境监测常见专业名词扫盲服务器平均负载服务器平均负载的定义如何判断平均负载值以及好坏情况如果依据平均负载来判断服务器当前状况系统平均负载和CPU使用率的区别 CPU上下文切换基本概念3种上下文切换进程上下文切换线程上下文切换中断上下文切换 查看上下文切…...
ELF文件结构
ELF文件结构 前文结尾说到编译器编译源代码后生成的文件叫做目标文件,而目标文件经过编译器链接之后得到的就是可执行文件。那么目标文件到底是什么?它和可执行文件又有什么区别?链接到底又做了什么呢?接下来,我们将探…...
【C++】有关string迭代器的几道OJ题详解
目录 一、字符串最后一个单词的长度 题目描述 完整代码 二、验证回文串 题目描述 完整代码 三、反转字符串 题目描述 完整代码 四、反转字符串中的单词 题目描述 完整代码 一、字符串最后一个单词的长度 原题链接 题目描述 计算字符串最后一个单词的长度ÿ…...
谷歌宣布向云计算客户开放 Gemini Pro,开发者可用其构建应用
12 月 14 日消息,美国时间周三,谷歌宣布了一系列升级的人工智能(AI)功能,旨在为其云计算客户提供更好的服务。这家科技巨头正试图赶上竞争对手,比如微软和 OpenAI,它们都在积极利用人工智能的热…...
软件测试用例经典方法 | 单元测试法案例
单元测试又称模块测试,是对软件设计的最小单元的功能、性能、接口和设计约束等的正确性进行检验,检查程序在语法、格式和逻辑上的错误,并验证程序是否符合规范,以发现单元内部可能存在的各种缺陷。 单元测试的对象是软件设计的最…...
Leetcode 2967. Minimum Cost to Make Array Equalindromic
Leetcode 2967. Minimum Cost to Make Array Equalindromic 1. 解题思路2. 代码实现 题目链接:2967. Minimum Cost to Make Array Equalindromic 1. 解题思路 这一题其实我的思路有点笨,多少有点暴力求解的意思。 显然,如果我们给出全部的…...
【数据结构】什么是堆?
🦄个人主页:修修修也 🎏所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 堆的概念及结构 堆的定义如下: n个元素的序列{k1,k2,...,kn}当且仅当满足以下关系时,称之为堆. 或 把这个序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
第7篇:中间件全链路监控与 SQL 性能分析实践
7.1 章节导读 在构建数据库中间件的过程中,可观测性 和 性能分析 是保障系统稳定性与可维护性的核心能力。 特别是在复杂分布式场景中,必须做到: 🔍 追踪每一条 SQL 的生命周期(从入口到数据库执行)&#…...
MyBatis中关于缓存的理解
MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
