Warm tip: This article is reproduced from serverfault.com, please click

Socket event after API call not recording data correctly in React app

发布于 2020-11-28 14:51:16

The App

I'm working on this App that loads data from the server and display it on a chart a display the total in a Gauge. The server also emits an event when a new data is store on the DB. In the App.tsx I do an API call and also starts listening to this event.

The Problem

The API call works fine, all data is loaded and displayed, the problem is within the socketio event callback. The events array is empty and the currentTotal is zero, that is the initial values. I believe the problem has to do with a bad use of useState hook, but I don't really know how to do any other way.

I've tried to put the socket.on() in another useEffect or even out of one at the same scope of the useState hook, but it just created a lot of renderings and the page ended up taking some seconds to load.


This is my App.tsx file

...
    const [events, setEvents] = useState<Event[]>([])
    const [currentTotal, setCurrentTotal] = useState<number>(0)

    useEffect(() => {
        api
            .get('/events')
            .then((response) => {
                const total = totals(response.data)
                setCurrentTotal(total)
                setEvents(response.data)
            })
            .catch()

        socket.on('event-created', (newEvent: Event) => {
            if (newEvent.rule_name === 'entering') {
                newEvent.total = currentTotal + 1
            } else {
                newEvent.total = currentTotal - 1
            }

            setCurrentTotal(newEvent.total)
            setEvents([...events, newEvent])
        })
    })
...

And this is my socket service in src/services/socketio.ts

import { io } from 'socket.io-client'

const socket = io('http://127.0.0.1:4001/')
export default socket
Questioner
Allan Presotto
Viewed
0
lissettdm 2020-11-29 00:35:56

try to use the state callback function. This way you can get the current value:

App.tsx

 setCurrentTotal((prev) => { 
   const val = newEvent.rule_name === 'entering' ? 1: -1;
   prev = prev + val:
   return prev;
 })
 setEvents((prev) => ([...prev, newEvent]))