Tratando de poner en marcha una aplicación de chat básica y teniendo problemas con repeticiones excesivas cuando se envía un mensaje. Aquí está el código aplicable para el cliente:
const [chatMessages, setChatMessages] = useState([]); const sendChat = (e) => { socket.emit("sendMessage", e.target.elements.message.value); } useEffect(() => { socket.on("receiveMessage", (chatMessage) => { setChatMessages([...chatMessages, chatMessage]); console.log(chatMessages); }); }, [chatMessages]); return ( {chatMessages.map((message) => <p>{message}</p>)} )Luego, en el servidor:
io.on("connection", (socket) => { socket.on("sendMessage", (chatMessage) => { console.log("message sent"); io.to(roomId).emit("receiveMessage", chatMessage); }); }Cuando hago esto, el mensaje se envía y recibe con éxito, pero sucede muchas veces (consola):
[] [] [{...}] [{...}] (2) [{...}, {...}]En el tercer mensaje esto es lo que se registra. En el sexto o séptimo mensaje, toda la página se detiene bruscamente, ya que se registra unas 100 veces.
He probado lo siguiente:
Tener una matriz de dependencias vacía en useEffect() . Esto soluciona los renderizados, pero presenta un nuevo problema. El último mensaje es el único que se guarda y reemplaza al último, por lo que solo puede ver un mensaje a la vez.
Sacándolo de useEffect() todos juntos. Esto solo empeora el problema y provoca aún más repeticiones por mensaje.
Cualquier ayuda sería apreciada. ¡Gracias!
Está creando controladores de eventos de socket cuando el estado de chatMessages se actualiza pero no los limpia . Si edita su código o el componente se vuelve a renderizar, etc... entonces se agrega otro controlador de eventos de socket. Los múltiples controladores comenzarán a acumularse y a poner en cola múltiples actualizaciones de estado inesperadas.
Además, dado que las actualizaciones de estado de React se procesan de forma asíncrona, no puede registrar el estado inmediatamente después de poner en cola la actualización y esperar ver el estado actualizado. Use un useEffect separado para esto.
Agregue una función de limpieza useEffect para eliminar el controlador de eventos y vuelva a incluir la matriz de estado de chatMessages actualizada en la devolución de llamada del controlador.
useEffect(() => { const handler = (chatMessage) => { setChatMessages([...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, [chatMessages]); Agregue una función de limpieza useEffect , elimine las dependencias para que el efecto se ejecute una vez en el montaje del componente y use una actualización de estado funcional para actualizar correctamente desde el estado anterior en lugar del estado inicial en el gabinete de devolución de llamada.
useEffect(() => { const handler = (chatMessage) => { setChatMessages(chatMessages => [...chatMessages, chatMessage]); } socket.on("receiveMessage", handler); return () => socket.off("receiveMessage", handler); }, []);Entre las dos la segunda opción es la solución más óptima, pero la que elijas es tu decisión.
Para registrar las actualizaciones de estado de chatMessages :
useEffect(() => { console.log(chatMessages); }, [chatMessages]);Dado que tiene una dependencia de chatMessages , cada vez que cambia chatMessages , crea un nuevo oyente. Y es por eso que se vuelve más y más lento a medida que ingresan más mensajes.
Podrías hacer dos cosas:
chatMessages de chat localmente dentro del método useEffect . Y puede difundir esa matriz y simplemente llamar a setChatMessages con la matriz extendida. Cuando haga esto, puede eliminar la dependencia de chatMessages para useEffect y aún tener todos los mensajes. Como buena práctica, debe devolver una función que elimine el detector de eventos cuando se desmonte el componente. const [chatMessages, setChatMessages] = useState([]); const sendChat = (e) => { socket.emit("sendMessage", e.target.elements.message.value); } useEffect(() => { let localMessages = []; const callback = (chatMessage) => { localMessages = [...localMessages, chatMessage]; setChatMessages(localMessages); console.log(localMessages); }; socket.on("receiveMessage", callback); return () => { socket.off("receiveMessage", callback); } }, []); return ( {chatMessages.map((message) => <p>{message}</p>)} )useRef para almacenar los valores. Sin embargo, eso no activará la nueva representación de la interfaz de usuario cuando cambie el valor y probablemente eso no sea lo que desea. Así que esta puede no ser una buena opción.