ReactHooks + Websocket

905 阅读2分钟

1.父组件引用

import WebsocketProvider from "./WebsocketProvider";

image.png

2.provider 组件封装

import React, { useContext, useEffect, useState } from "react";
// import useRouter from "use-react-router";
import { notification } from "antd";
import closeImg from "@assets/images/home/closeIcon.png";
import newOrderIcon from "@assets/images/home/newOrderIcon.png";
import { UserContext } from "@src/auth/index";
let websocket,
  lockReconnect = false;
let retryCount = 0;

interface WebsocketProps {
  children: React.ReactNode;
}
let updateOrderNumberTimer = null
const WebsocketProvider: React.FC<WebsocketProps> = ({ children }) => {
  const { Provider } = React.createContext();
  // const {getOrderNumber} = useContext(UserContext)
  // 这里如果以后要用跳转,需要重写,不用provider,单独写个js,用输入输出数据的形式,比如在src/index.tsx, 初始化的形式, 如果使用useRouter, 会全局监听路由变化,并强制刷新组件
  // const {history} = useRouter();
  const bLanguage = localStorage.getItem("bLanguage");
  const isEn = bLanguage === "en";
  let wsBaseUrl = ""; //websocket 基础路径
  let openFlag = false;
  let initFlag = "init";
  //websoket启动
  if (process.env.DEPLOY_ENV === "prod") {
    wsBaseUrl = "wss://gows-api.funpinpin.com";
  } else if (process.env.DEPLOY_ENV === "staging") {
    wsBaseUrl = "wss://go-ws-api.staging.funpinpin.top";
  } else {
    wsBaseUrl = "wss://go-ws-api.test.funpinpin.top";
  }
  const wsUrl = `${wsBaseUrl}/ws?domain=${localStorage.getItem("shopDomain") ||
  window.location.hostname}`;


  // 更新订单数量
  // function updateOrderNumber () {
  //   let count = 0
  //   if(updateOrderNumberTimer) {
  //     clearInterval(updateOrderNumberTimer)
  //   }
  //   updateOrderNumberTimer = setInterval(()=>{
  //     if(count>2){
  //       clearInterval(updateOrderNumberTimer)
  //     } else {
  //       getOrderNumber()
  //     }
  //     count++
  //   },2000)
  // }

  useEffect(() => {
    initFlag = "no_init";
    createWebSocket(wsUrl);
  }, []);

  useEffect(() => {
    /*切换语言 & 不是第一次渲染 的时候,重新弹出这个提示*/
    if (
      document.getElementsByClassName("new-order-notification").length > 0 &&
      initFlag !== "init"
    ) {
      notification.open({
        className: "new-order-notification",
        message: "",
        placement: "bottomRight",
        bottom: 50,
        duration: null,
        description: notitionMessage(),
        key: "initKey"
      });
    }
  }, [bLanguage]);

  //提示内容
  const notitionMessage = () => {
    return (
      <div className="flex flex-space-between">
        <div>
          <img
            width={16}
            style={{ marginRight: "8px" }}
            src={newOrderIcon}
            alt=""
          />
          <span
            onClick={() => {
              // history.push("/orders");
            }}
          >
            {isEn ? "New order for you" : "您有新订单"}
          </span>
        </div>
        <img
          width={10}
          onClick={() => {
            notification.close("initKey");
            openFlag = false;
          }}
          style={{ cursor: "pointer" }}
          src={closeImg}
          alt=""
        />
      </div>
    );
  };
  //新订单消息提示
  const openNotification = () => {
    openFlag = true;
    //保证一次只出现一次弹窗
    // if (document.getElementsByClassName("new-order-notification").length === 0) {
    if (openFlag) {
      notification.open({
        className: "new-order-notification",
        message: "",
        placement: "bottomRight",
        bottom: 50,
        duration: null,
        description: notitionMessage(),
        key: "initKey"
      });
    }
  };
  const createWebSocket = wsUrl => {
    //readyState  0:连接尚未建立  1:连接已经建立  2:连接正在关闭 3:连接已经关闭或不可用
    retryCount++;
    websocket = new WebSocket(wsUrl);
    websocket.onopen = function() {
      heartCheck.reset().start();
    };
    websocket.onerror = function() {
      reconnect(wsUrl);
    };
    websocket.onclose = function() {
      reconnect(wsUrl);
    };
    websocket.onmessage = function(e) {
      lockReconnect = true;
      retryCount = 0; //每次能收到消息 就代表是可以连接状态,不会陷入死循环,这时将变量重置为0
      //e 为服务端传输的消息,在这里可以处理
      try {
        let data = JSON.parse(e.data);
        if (data?.id === 22) {
          // updateOrderNumber()
          if (!openFlag) {
            openNotification(); //新订单提醒
          }
        }
      } catch (e) {
        return false;
      }
    };
  };
  const reconnect = wsUrl => {
    //当前端建立新的连接超过5次以后,后端还没响应,前端就停掉了, 避免死循环
    if (retryCount > 5) return;
    //没连接上会一直重连,设置延迟避免请求过多
    setTimeout(function() {
      createWebSocket(wsUrl);
      lockReconnect = false;
    }, 4000);
  };
  const heartCheck = {
    timeout: 30000, //30秒(后端设置的是60秒没收到ping,服务器会断开,所以这里不能超过60s)
    timeoutObj: null,
    serverTimeoutObj: null,
    reset: function() {
      clearInterval(this.timeoutObj);
      return this;
    },
    start: function() {
      this.timeoutObj = setInterval(function() {
        //这里发送一个心跳,后端收到后,返回一个心跳消息,
        //onmessage拿到返回的心跳就说明连接正常
        if (websocket?.readyState === 1) {
          websocket?.send("ping");
        }
      }, this.timeout);
    }
  };
  //关闭连接
  const closeWebSocket = () => {
    websocket?.close();
  };

  return <Provider value={{}}>{children}</Provider>;
};

export default WebsocketProvider;