简体   繁体   English

Next.js (React.js),带有 Node.js 服务器 Socket.io 客户端多个 ID

[英]Next.js (React.js), with Node.js server Socket.io client multiple IDs

I'm having an issue where my React app is outputting the same one ID on every page that it loads, where as Backend Node.js Socket.io server is outputting multiple client IDs whenever I change route in my app...我遇到了一个问题,我的 React 应用程序在它加载的每个页面上输出相同的 ID,而后端 Node.js Socket.io 服务器在我更改应用程序中的路由时输出多个客户端 ID...

Server:服务器:

const io = new Server(httpServer, {
  cors: {
    origin: "http://localhost:3000",
  },
});
io.on("connection", (socket) => {
  console.log("client connected: ", socket.id);

  socket.on("disconnect", (reason) => {
    console.log("disconnect", reason);
  });
});

App.js:应用程序.js:

 useEffect(() => {
    console.log("use effect", socket.id);

    return () => {
      socket.off("connect");
      socket.off("disconnect");
    };
  }, []);

Socket.ts:套接字.ts:

import io from "socket.io-client";

const socket = io(`${process.env.NEXT_PUBLIC_SOCKET_IO_URL}`);
socket.on("connect", () => console.log("socket_id", socket.id));
export default socket;

server.js (backend - websocket) server.js(后端 - websocket)

const io = new Server(httpServer, {
  cors: {
    origin: "http://localhost:3000",
  },
});
io.on("connection", (socket) => {
  console.log("client connected: ", socket.id);

  socket.on("disconnect", (reason) => {
    console.log("disconnect", reason);
  });});

后端服务器日志

前端日志

First of all, socket.io server sometimes generates a new id due reconnections or others things:首先,socket.io 服务器有时会由于重新连接或其他原因生成新的 id:

https://socket.io/docs/v4/server-socket-instance/#socketid https://socket.io/docs/v4/server-socket-instance/#socketid

So if you are planning keep the same id, i disencourage you.因此,如果您打算保持相同的身份,我不鼓励您。

Well, but looks you are wondering about fast recriation of ids.好吧,但是看起来您想知道如何快速重新创建 id。 React is rendering according state changes, so create a stateless connection inside some component cause this behaviour. React 正在根据 state 更改进行渲染,因此在某些组件内创建无状态连接会导致此行为。 Yon can choose a lot of solutions but, i will present you a solution extensible, ease to mantain and deliver to componets the role of subscribe and unsubscribe to itself listeners, this sounds better than have a global listeners declaration:D Yon 可以选择很多解决方案,但是,我将向您展示一个可扩展的解决方案,易于维护并向组件提供订阅和取消订阅自身侦听器的角色,这听起来比拥有全局侦听器声明更好:D

1. Create Socket Context We will use useContext hook to provide SocketContext to entire app. 1. 创建 Socket 上下文我们将使用useContext钩子为整个应用程序提供 SocketContext。

Create a file in context/socket.js :context/socket.js创建一个文件:

import React from "react"
import socketio from "socket.io-client"
import { SOCKET_URL } from "config"

export const socket = socketio.connect(SOCKET_URL)
export const SocketContext = React.createContext()

2. Use socket context and provide a value Add SocketContext provider at the root of your project or at the largest scope where socket is used: 2. 使用套接字上下文并提供一个值添加 SocketContext 提供程序在您的项目的根目录或使用套接字的最大 scope 处:

import {SocketContext, socket} from 'context/socket';
import Child from 'components/Child';

const App = () => {
  return (
    <SocketContext.Provider value={socket}>
      <Child />
      <Child />
      ...
    </SocketContext.Provider
  );
};

3. Now you can use socket in any child component For example, in GrandChild component, you can use socket like this: 3. 现在你可以在任何子组件中使用socket 例如在GrandChild组件中,你可以这样使用socket:

import React, {useState, useContext, useCallback, useEffect} from 'react';
import {SocketContext} from 'context/socket';

const GrandChild = ({userId}) => {

  const socket = useContext(SocketContext);

  const [joined, setJoined] = useState(false);

  const handleInviteAccepted = useCallback(() => {
    setJoined(true);
  }, []);

  const handleJoinChat = useCallback(() => {
    socket.emit("SEND_JOIN_REQUEST");
  }, []);


  useEffect(() => {
    // as soon as the component is mounted, do the following tasks:

    // emit USER_ONLINE event
    socket.emit("USER_ONLINE", userId); 

    // subscribe to socket events
    socket.on("JOIN_REQUEST_ACCEPTED", handleInviteAccepted); 

    return () => {
      // before the component is destroyed
      // unbind all event handlers used in this component
      socket.off("JOIN_REQUEST_ACCEPTED", handleInviteAccepted);
    };
  }, [socket, userId, handleInviteAccepted]);

  return (
    <div>
      { joined ? (
        <p>Click the button to send a request to join chat!</p>
      ) : (
        <p>Congratulations! You are accepted to join chat!</p>
      ) }
      <button onClick={handleJoinChat}>
        Join Chat
      </button>
    </div>
  );
};

What is useContext ?什么是useContext

useContext provides a React way to use global state, You can use context in any child component, Context values are states. useContext 提供了一种使用全局 state 的 React 方式,您可以在任何子组件中使用上下文,上下文值是状态。 React notices their change and triggers re-render. React 注意到它们的变化并触发重新渲染。

What is useCallback ?什么是useCallback Why did you put every handlers inside useCallback ?为什么将每个处理程序都放在useCallback useCallback prevents reassigning whenever there is state update Functions will be reassigned only when elements in the second argument are updated每当有 state 更新时, useCallback阻止重新分配函数只会在第二个参数中的元素更新时重新分配

More reference: https://www.w3schools.com/react/react_usecallback.asp更多参考: https://www.w3schools.com/react/react_usecallback.asp

This nice tutorial has obtained in: https://dev.to/bravemaster619/how-to-use-socket-io-client-correctly-in-react-app-o65这个不错的教程已获得: https://dev.to/bravemaster619/how-to-use-socket-io-client-correctly-in-react-app-o65

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM