该系列其他文章可以点击查看专栏👉🏻👉🏻react-query手把手教程系列
场景
在一般情况下,网页发送HTTP请求,服务器对该请求进行响应。一旦结束后该连接就会被关闭。如果你需要最新的数据,那就需要按照需求不断地对服务器进行轮询。
如果你需要和浏览器进行实时通讯,就需要使用浏览器提供的websocket。它允许浏览器与服务器进行长连接,能够实时与后端发送并接收数据。
如果你只是简单的搭建一个应用,那么后端只需要使用socket.io即可,如果需要在高可用的生产环境,可以参考使用声网相关的服务。
在前端浏览器对于websocket的支持程度也不一致,所以需要一些兼容的代码来磨平这种差异。而且内置的websocket客户端不支持自动重连或者自动重试等特性,因此客户端你也可以使用socket.io(你没看错,该库同时对前后端提供了相关的解决方案)。
使用websocket进行实时更新
下面将模拟websocket推送给前端指令,让前端对react-query的某个查询进行更新的需求:
为了让展示的内容更加直观,下面的代码将会从uuid.rocks获取一个随机的uuid数据,变化后就能清楚的知道前端已经更新,封装的组件如下:
const RandomID = () => {
const randomIdQuery = useQuery(
["random"],
() => fetch(`https://uuid.rocks/plain`)
.then(res => res.text())
)
return (
<div>
<h1>Random ID</h1>
{randomIdQuery.isLoading ? (
<p>Loading...</p>
) : (
<p>{randomIdQuery.data}</p>
)}
</div>
)
}
接着创建一个websocket客户端,为了简化教程,这里直接采用浏览器内置的websocket。下面的代码创建一个websocket实例👇🏻
const ws = new WebSocket("wss://websocket-echo.glitch.me");
例子中的websocket后端服务,将会实时返回前端发送的数据,具体可点击该链接查看👉🏻websocket-echo
为了模拟来自websocket服务器的消息,下面将会创建一个钩子,每秒发送一次当前的查询键
PS:如果是真实的后端,此时应该是发送给前端需要更新的查询键,这里为了模拟后面的例子写死传入的是
["random"]
function useSendTimedMessage(queryKey) {
useEffect(() => {
const interval = setInterval(() => {
ws.send(JSON.stringify(queryKey))
}, 1000)
return () => clearInterval(interval)
}, [queryKey])
}
最后还需要一个钩子,来监听从服务器实时获取的数据,一旦获取数据成功后,使用queryClient.invalidateQueries方法让后端指定的react-query的查询数据数据过期,以便模拟触发更新操作。
function useWebsocketQueryInvalidate() {
const queryClient = useQueryClient()
useEffect(() => {
function handleMessage(event) {
const queryKey = JSON.parse(event.data)
queryClient.invalidateQueries(queryKey)
}
ws.addEventListener('message', handleMessage)
return () => ws.removeEventListener('message', handleMessage)
}, [queryClient])
}
因此整体代码如下:
import { useEffect } from "react";
import { useQuery, useQueryClient } from "react-query";
const ws = new WebSocket("wss://websocket-echo.glitch.me");
function useSendTimedMessage(queryKey) {
useEffect(() => {
const interval = setInterval(() => {
ws.send(JSON.stringify(queryKey));
}, 1000);
return () => clearInterval(interval);
}, [queryKey]);
}
function useWebsocketQueryInvalidate() {
const queryClient = useQueryClient();
useEffect(() => {
function handleMessage(event) {
const queryKey = JSON.parse(event.data);
queryClient.invalidateQueries(queryKey);
}
ws.addEventListener("message", handleMessage);
return () => ws.removeEventListener("message", handleMessage);
}, [queryClient]);
}
const RandomID = () => {
const randomIdQuery = useQuery(["random"], () =>
fetch(`https://uuid.rocks/plain`).then((res) => res.text())
);
useSendTimedMessage(["random"]);
useWebsocketQueryInvalidate();
return (
<div>
<h1>Random ID</h1>
{randomIdQuery.isLoading ? (
<p>Loading...</p>
) : (
<p>{randomIdQuery.data}</p>
)}
</div>
);
};
export default RandomID;
实际效果可以看到,该页面的数据每秒钟都会刷新一次。点击查看效果
如果将服务端换成真正有相关逻辑的websocket接口,那么你将会看到,前端根据后端实时发送的指令,定向更新当前页面的某些接口。
这样如果你需要展示实时数据,就不用对后端进行轮询操作,而是通过后端发送的实时指令来进行更新。
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 25 天,点击查看活动详情