react-query手把手教程17-websocket

949 阅读3分钟

该系列其他文章可以点击查看专栏👉🏻👉🏻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 天,点击查看活动详情