Tengo una lista de mensajes y agrego un mensaje en vivo recibido del socket, un objeto insertado desde el socket, pero después de eso, siempre reemplaza el último en lugar de insertar uno nuevo.
const [chatList, setChatList] = useState(props.chatList ? props.chatList : []); useEffect(() => { const messageListener = (message) => { console.log('message',message); console.log('chatList',chatList); if(message.conversation_id == props.conversation_id){ const updatedMsgs = [...chatList,message]; setChatList(updatedMsgs); } }; socket.on('myEventName', messageListener); return () => { socket.off('myEventName', messageListener); }; }, [props.conversation_id]); El nuevo mensaje y el registro de la lista de mensajes se ven como 
Parece un recinto obsoleto del estado chatList . Use una actualización de estado funcional para actualizar desde el estado anterior y no el valor de estado cerrado en el alcance de devolución de llamada, setChatList(list => [...list, message]); .
useEffect(() => { const messageListener = (message) => { console.log('message',message); console.log('chatList',chatList); if (message.conversation_id == props.conversation_id) { setChatList(list => [...list, message]); } }; socket.on('myEventName', messageListener); return () => { socket.off('myEventName', messageListener); }; }, [props.conversation_id]);Este problema ocurre debido a los Stale Closures en Javascript.
Los ganchos dependen en gran medida de los cierres de JavaScript. Es por eso que los ganchos son tan expresivos y simples. Pero los cierres a veces son complicados.
Aquí está el ejemplo:
function DelayedCount() { const [count, setCount] = useState(0); function handleClickAsync() { setTimeout(function delay() { setCount(count + 1); }, 1000); } return ( <div> {count} <button onClick={handleClickAsync}>Increase async</button> </div> ); }Si hace clic rápidamente en el botón 2 veces, aumentará el recuento solo en 1, en lugar de 2.
En cada clic setTimeout(delay, 1000) programa la ejecución de delay() después de 1 segundo. delay() captura la count variable como 0.
Ambos delay() closures (porque se han hecho 2 clics) actualizan el estado al mismo valor: setCount(count + 1) = setCount(0 + 1) = setCount(1) .
Todo porque el delay() closure del segundo clic ha capturado la variable de recuento obsoleta como 0.
Para solucionar el problema, necesitamos usar una forma funcional setCount(count => count + 1) para actualizar el estado de conteo:
... function handleClickAsync() { setTimeout(function delay() { setCount(count => count + 1); }, 1000); } ...Ahora el botón funciona como se esperaba.
Aquí está el artículo completo que puede ayudarlo a comprender acerca de los Stale Closures . Enlace del artículo