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
Advertisement
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]);