WebSocket + React 的简单 Demo
或许别人也有,这里也不见得更好
关于需求
简单接受服务端信息的功能
前端需求分析
- 可接受服务端信息
- 可关闭会话
- 可重启会话
知识需求
- 一点点 TS + React
- 一点点 node +express
- 一点点 websocket
关于最简 demo
- 前端仅实现
启动 WebSocket
与接受会话
为关键 - 服务端 node 会实现计时器里实现不断发送信息的任务
import React, { useState, useRef, useLayoutEffect, useEffect } from 'react';
const App = () => {
const ws = useRef(null);
const [message, setMessage] = useState('');
//启动
useLayoutEffect(() => {
ws.current = new WebSocket('ws://localhost:7070');
ws.current.onmessage = e => {
setMessage(e.data);
};
return () => {
ws.current?.close();
};
}, [ws]);
return (
{message}
);
};
关于主动会话与主动开关机制
熟悉一下 相关方法
- WebSocket.send() 发送会话
- WebSocket.close() 关闭会话
- WebSocket.onmessage() 接受会话
在例子里,组件渲染的时候已经开始了 WebSocket 链接,根据需求是要主动开启 WebSocket 链接的,例子关键 WebSocket 需要提取出来
const webSocketInit = useCallback(() => {
if (!ws.current) {
ws.current = new WebSocket('ws://localhost:7070');
}
}, [ws]);
React 是喜欢不可变数据的,这里使用了 useCallback 包裹了相关方法,以达到可主动链接且不会造成多次渲染的目的
在组件生命周期外,或许需要关闭会话,达到浏览器资源释放的问题
useLayoutEffect(()=>{
// do somting
return ()=>{
ws.current?.close();
}
},[ws])
React Hooks Api 内建议这样释放资源,同理可以在 commpoent api 内使用 xxx 释放资源
关于最终展示的代码
个人认为在最终代码内,最好有日志打印,使用 Hooks api 来监听 WebSocket 的状态去打印日志会显得很费劲且繁琐不堪,得益于 WebSocket 自有的 api 就可以做到很好的日志答应
import React, { useState, useRef, useLayoutEffect, useCallback } from 'react';
import Header from './components/header';
import './App.less';
const App = () => {
const ws = useRef(null);
const [message, setMessage] = useState('');
const [readyState, setReadyState] = useState('正在链接中');
const [rdNum, SetRdNum] = useState(0);
/**
* 伪随机函数,测试用
* */
const getRandomInt = useCallback(() => {
SetRdNum(Math.floor(Math.random() * Math.floor(999)));
}, []);
const webSocketInit = useCallback(() => {
const stateArr = [
'正在链接中',
'已经链接并且可以通讯',
'连接正在关闭',
'连接已关闭或者没有链接成功',
];
if (!ws.current || ws.current.readyState === 3) {
ws.current = new WebSocket('ws://localhost:7070');
ws.current.onopen = _e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onclose = _e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onerror = e =>
setReadyState(stateArr[ws.current?.readyState ?? 0]);
ws.current.onmessage = e => {
setMessage(e.data);
};
}
}, [ws]);
/**
* 初始化 WebSocket
* 且使用 WebSocket 原声方法获取信息
* */
useLayoutEffect(() => {
getRandomInt();
webSocketInit();
return () => {
ws.current?.close();
};
}, [ws, getRandomInt, webSocketInit]);
console.log('ws.readyState', ws.current?.readyState);
return (
{message}
{/* {readyState} */}
{
ws.current?.close();
}}>
Clone
{
getRandomInt();
webSocketInit();
}}>
start
{
if (ws.current?.readyState !== 1) {
console.log('尚未链接成功');
setMessage('正在链接');
return;
}
ws.current?.send(rdNum.toString());
}}>
ID:{rdNum}
);
};
export default App;
关于 QA
Q:为什么不实用 socket.io?
简单需求下,没必要下载一个库,学习 WebSocket 也有利于理解 socket 源码,或许后期也会在项目使用 socket.io
Q:为什么要在 function commopent 内里做 WebSocket 初始化?
可以肯定,在外初始化肯定是更好阅读与理解的,如下例子:
import React from 'react';
const ws = new WebSocket('ws://loaclhost:7070');
const App = () => {
// do ing...
};
考虑到后续的 WebSocket 状态均需要在 function commopent 内显示,所以最终选择了在内初始化的选择,目前还没看到优秀的在外且可接受状态的优秀例子
Q:为什么不能直接使用 WebSocket.readyState 的状态来打印状态?
WebSocket.readyState 实际上并没有跟着返回消息或者操作更新了状态,需要主动获取来感知状态的更新
关于小结
本文或许没多到新鲜感,属于个人笔记的流水线文章
感谢各位阅读!