不能在事件回调函数上使用 socketio 中的反应状态

Can't use react state inside a socketio on event callback function

我正在尝试制作一个组件,当您添加任务时,事件侦听器会添加到 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>
    );
}

您必须使用 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);
    })
}