Estoy tratando de crear un componente donde, cuando agrega una tarea, se agrega un detector de eventos al cliente socketio. El evento se activa cuando el servidor informa el progreso de la tarea y quiero actualizar la tarea con el nuevo progreso.
El problema es que cuando actualizo el progreso de la tarea, la función de devolución de llamada del evento usa el estado del componente cuando se asignó la función. Si el estado de "tareas" estaba vacío antes de la asignación, la actualización de progreso borra el estado.
No sé cómo puedo usar el estado actual dentro de la función de devolución de llamada. (La actualización de progreso funciona fuera de la devolución de llamada del evento)
Aquí hay un ejemplo reproducible mínimo:
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> ); }Debe usar el setter con devolución de llamada para obtener el valor actual del estado:
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); }) }