【问题标题】:Socketio object undefined when it's passed to child component in React, typescriptSocketio对象在React中传递给子组件时未定义,打字稿
【发布时间】:2021-03-22 23:51:30
【问题描述】:

我有这段代码,它基本上是一个 WIP 页面,用于我正在开发的简单聊天应用程序,作为我的教师课程的一部分。

我正在从服务器获取套接字,并且可以在主组件上毫无问题地使用它,但在子组件中它始终是 undefined

主要组件:

import React, { FormEvent } from 'react';
import { asyncConnect } from '../utils/index';
import { 
  socketEvents, 
  SendActiveUsersMessage
} from '../types';
import { Socket } from 'socket.io-client';
import Home from './Home';

export default function RegisterUser() {  

  let socket: typeof Socket;
  const [username, setUsername] = React.useState<string>("");
  const [badUsername, setBadUsername] = React.useState<boolean>(true);
  const [errorMsg, setErrorMsg] = React.useState<string>("");
  const [signupSuccessful, setSignupSuccesful] = React.useState<boolean>(false);
  
  const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {

    event.preventDefault();

    if (username === "") {
      setBadUsername(true)
      return;
    } else {
      setBadUsername(false);
    }

    socket = await asyncConnect();
    if(!socket) {
      setErrorMsg('Backend unreachable');
    }

    socket.emit(socketEvents.SEND_USERNAME, {
      username
    });

    socket.on(socketEvents.SEND_ACTIVE_USERS, (msg: SendActiveUsersMessage) => {
      console.log('activeUsers: ', msg.activeUsers);
    });
    
    setSignupSuccesful(true);

  }

  const handleInputChange = (e: React.FormEvent<HTMLInputElement>) => {
    setUsername(e.currentTarget.value);
  }
  
  const generateErrorMessageIfNeeded = () => {
    
    let error = errorMsg;
    
    if (badUsername) {
      error += "User name must not be empty\n";
    }
    return (
      <h3>{error}</h3>
    )

  }

  const generateInputForm = () => {
    return (
      <div>
        <form onSubmit={handleSubmit}>
          <h2>Enter username</h2>

          <input 
            type='text'
            id='username'
            onChange={handleInputChange} 
          />
          <button type="submit">Submit</button>
        </form>
        <div>{generateErrorMessageIfNeeded()}</div>
      </div>
    )
  }

  const render = () => {
    if(signupSuccessful) {
      return <Home socket={socket as typeof Socket}/>
    } else {
      return generateInputForm();
    }
  }
  return render();
}

asyncConnect 函数如下所示:

import io, { Socket } from 'socket.io-client';
import { socketEvents } from '../types';

export const asyncConnect = async():Promise<typeof Socket> => {
  return new Promise( (resolve, _reject) => {
    setTimeout(() => {
      const socket = io.connect(process.env.REACT_APP_SERVER_URL as string);
      socket.on(socketEvents.CONNECT, () => {
          resolve(socket)
      });
    }, 500)
  });
}

Home 组件,我将套接字传递到该组件,如下所示:

import { Socket } from "socket.io-client"

interface Props {
  socket: typeof Socket
}

export default function Home (props: Props) {  
  
  console.log('props from Home component: ', props);
  return (    
    <h2>Home </h2>
  )
}

将套接字保存到状态

我尝试过的另一件事是为套接字创建状态(类似于 4 个已经存在的状态)。但是当我尝试这样做时,在我保存状态并尝试使用状态内部的套接字后,我得到socket.emit is not a function 错误。

感谢任何形式的帮助,因为我不知道我做错了什么。

【问题讨论】:

    标签: reactjs typescript socket.io react-state-management


    【解决方案1】:

    在触发表单提交事件之前,您不会将套接字初始化为一个值。即使由于 React 的工作方式而被初始化,如果组件被重新渲染,它也会丢失,因为它没有作为状态持久化。

    这是使用 socket.io 的一种不同寻常的方式。感觉就像您正在尝试将其插入您通常使用 ajax 的地方。通常你会期望在接近页面加载时初始化一个 websocket 以开始监听任何潜在的服务器推送事件。我会说你应该考虑在你的反应组件之外初始化 websocket 并将其传递给初始调用。您通常不希望组件的渲染执行诸如启动持久连接之类的操作。

    考虑迁移到使用回调来触发事件而不是传递 websocket 的模式。这有助于保持组件简单且定义明确。

    return &lt;Home submitForm={submitForm}/&gt;

    【讨论】:

    • 我现在已经用useEffect() 钩子实现了它,在安装的第一个组件中,一切似乎都工作正常。但是每当我重新加载页面时,我仍然会创建新的套接字,虽然我知道这是预期的行为,这是正确的方法吗?我尝试使用 redux 存储来永久保存套接字,但它们不能保存到本地存储,因为它们具有循环引用,并且应该处理的库无法正常工作。
    • 您无法跨页面加载保存套接字。套接字不是由您的应用程序管理的状态,因此将它们视为错误。
    猜你喜欢
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 2021-11-10
    • 2022-08-18
    • 2017-09-19
    • 1970-01-01
    • 2018-05-13
    • 1970-01-01
    相关资源
    最近更新 更多