无法在事件回调函数中使用socketio内部的react状态

问题描述 投票:0回答:2

我正在尝试制作一个组件,当您添加任务时,事件侦听器会添加到 socketio 客户端。当服务器报告任务进度并且我想用新进度更新任务时,会触发该事件。

问题是,当我更新任务进度时,事件回调函数使用函数分配时组件的状态。如果“任务”状态在分配之前为空,则进度更新会删除该状态。

我不知道如何在回调函数中使用当前状态。 (进度更新在事件回调之外起作用)

这是一个最小的可重现示例:

import React, {useEffect, useRef, useState} from "react";
import axios from "axios";
import socketIOClient from "socket.io-client"


export default function TestComponent() {
    const socketRef = useRef(null);
    const [tasks, setTasks] = useState([])  // task: {id, name, time_start, progress}

    useEffect(() => {
        if (socketRef.current == null) {
            socketRef.current = socketIOClient({path: "/api/socket.io"});
        }

        return () => {
            socketRef.current.disconnect();
        };
    }, []);

    function addTask(task) {
        setTasks([...tasks, task]);
        addTaskProgressEvent(task);
    }

    function addTaskProgressEvent(task) {
        const event = "task:" + task.id + ":progress";

        socketRef.current.on(event, (progress) => {
            // update task progress (this failes!)
            setTasks(tasks.map(list_task => list_task.id === task.id ?
                {...list_task, progress: progress} : list_task))

            console.log(progress);
        })
    }

    function handleStartTask() {
        axios.post("/api/task").then((response) => {
            addTask(response.data);
        });
    }

    return (
        <div className="TestComponent">
            <button onClick={handleStartTask}>
                start task
            </button>
        </div>
    );
}
javascript reactjs react-hooks socket.io
2个回答
2
投票

您必须使用带有回调的setter,才能获取状态的当前值:

function addTaskProgressEvent(task) {
    const event = "task:" + task.id + ":progress";

    socketRef.current.on(event, (progress) => {
        // update task progress (this failes!)
        setTasks(oldTasks => oldTasks.map(list_task => list_task.id === task.id ?
            {...list_task, progress: progress} : list_task))

        console.log(progress);
    })
}

0
投票

试试这个

useEffect(() => {
if (!socketInstance) return; // if no socket instance is created return 
 from here
// if user has connections then update connection list with received 
 connections status update
socketInstance.on("getOnlineUsers", (data) => {
  setConnections((oldConnections) => {
    return oldConnections.length
      ? oldConnections
          .filter((c) => c._id === data.userId)
          .map((c) => {
            c.isOnline = "1";
            return c;
          })
      : oldConnections;
  });
});

socketInstance.on("getOfflineUsers", (data) => {
  setConnections((oldConnections) => {
    return oldConnections.length
      ? oldConnections
          .filter((c) => c._id === data.userId)
          .map((c) => {
            c.isOnline = "0";
            return c;
          })
      : oldConnections;
  });
});

// use socket to listen multiple events

// TODO listen to broadcast message from server & only update messages 
   array if upcoming message is for current user
socketInstance.on("message", (data) => {
  console.log("socket message event", data);
});

return () => {
  // remove socket event listeners once component unmounted
  socketInstance.off("connect");
};
}, [socketInstance]);
© www.soinside.com 2019 - 2024. All rights reserved.