Socket listener not getting updates from React state



I have a component in which I set my state from the passed parameters to the component

function ChatList({ bookingList, session, setActiveChat, setBookingList, socket }){

const [activeList, setActiveList] = useState(bookingList);
const [defaultList, setDefaultList] = useState(bookingList);

I set the activeList state from the passed params and then on click I update the state in order to show filtered results

    const changeTab = (index) => {
    if(index === 0){
        setActiveList(defaultList);
        if(defaultList.length > 0){
            setActiveChat(defaultList[0]);
        }else{
            setActiveChat(null);
        }
    }else {  
        let result = bookingList.filter(booking => booking.is_service_provider == false);
        setActiveList(result);
        if(result.length > 0){
            setActiveChat(result[0]);
        }else{
            setActiveChat(null);
        }
    }

So ultimately users can filter their chat lists based on the chosen index

And everything works fine, the DOM is updated and if I call the state from a regular function it shows the correct state.

But when I try to update the state from a socket listener I get the original state and not the filtered state

    useEffect(() => {
    socket.on('notification', (payload) => {

        let list_index = activeList.findIndex(obj => obj.id === payload.room);

        if(list_index > -1){
            let copy = [...activeList];
            copy[list_index].latest_message = payload.message;
            setActiveList(copy);
        }else{
            //Handle if user isnt in the correct segment
        }
            
    });
},[activeList])

The problem here is the activeList should only have 2 elements after being filtered but in the socket listener it gets the activeList with all the elements as it was before it gets filtered.

I even tested this with a random onclick listener, the onclick function gets the correct state, so why doesn’t my socket listener get the updated state?

Thanks

Answer

Whenever activeList changes you add a listener but you do not remove the previous handler. It might casue you

So, try using the return of useEffect.

useEffect(() => {
  function handler(payload) {....}

  socket.on('notification', handler);

  return () => socket.off('notification', handler);
}, [activeList]);

In addition when you just want to update the activeList the setter function gets the previous value so you can just use that, and then your effect won’t need any dep other than the ref to the setter function.

useEffect(() => {
  function handler(payload) {
    ...
    setActiveList((prev) => {
       const index = prev.findIndex(obj => obj.id === payload.room);
       if (...) return [];
       
       return [];
    });

  }

  socket.on('notification', handler);

  return () => socket.off('notification', handler);
}, [setActiveList]);


Source: stackoverflow