hiad
hiad

Reputation: 398

useEffect socket.on problem rerendering useState

I'm looking for some help using socket.io-client. I'm having some issues trying to get all messages from the chat server. I'm supposed to get an individual message and I'll need to push to a state. I'm not getting rerender on each message. Is there any way to handle the state and update it? Thanks

import React, {useState, useEffect} from 'react'
import {withRouter} from 'react-router-dom'
import Button from '../core/Button'
import Field from '../core/Field'
import Label from '../core/Label'
import {Container, ScrollContainer} from './StyledComponents'
import useLocalStorageState from '../../hooks/useLocalStorage'
import io from 'socket.io-client'
import {SOCKET_SERVER_CLIENT} from '../../utils/const'
import Profile from '../Profile/Profile'

let socket
const Chat = ({location}) => {
  const [listMessages, setListMessages] = useState([])
  const [message, setMessage] = useState('')

  useEffect(() => {
    const username = location.search.split('=')[1]
    socket = io(`${SOCKET_SERVER_CLIENT}/?username=${username}`)
    return () => {
      socket.disconnect()
    }
  }, [])

  useEffect(() => {
    socket.on('message', message => {
        const auxArr = listMessages
        auxArr.push(message)
        setListMessages(auxArr)
    })
  })

  return (
    <Container>
      {console.log('rerender')}
      <ScrollContainer>
        {listMessages &&
          listMessages.map(infoMessage => (
            <Profile key={infoMessage.time} {...infoMessage} />
          ))}
      </ScrollContainer>
    </Container>
  )
}

export default withRouter(Chat)

Upvotes: 0

Views: 221

Answers (1)

Nicholas Tower
Nicholas Tower

Reputation: 84912

const auxArr = listMessages
auxArr.push(message)
setListMessages(auxArr)

This code is mutating the old array, and then setting state with that same array. Function components do a check when setting state to make sure it changed. Since it's the same array before and after, the render gets skipped.

Instead of mutating the state, you will need to copy it:

const auxArr = [...listMessages];
auxArr.push(message);
setListMessages(auxArr);

Since you're basing the new state on the old one, you should also use the callback form of setting state, to eliminate any bugs that might happen if multiple set states happen at right about the same time:

setListMessage(prevMessages => {
  const auxArr = [...prevMessages];
  auxArr.push(message);
  return auxArr;
})

Upvotes: 1

Related Questions