useEffect, useState, and order of execution
I'm working on a fun little utility to make SVG morph animations and performance has become an issue with complex SVGs so I decided this is a good opportunity to properly learn about promises, async, await, etc. to avoid freezing the main thread (I've only been using React for the past couple of months and only really got into Javascript/Typescript since August). I have an onClick function that updates a state variable with the new SVG as string and then a useEffect where all the heavy processing happens. I hadn't worked on this project for a month and forgot that I did things in such a wierd way, so I initially made the onClick async and put a try catch finally in it. It actually mostly did what I wanted (the loading spinner which I turn off in the finally spins until the process is complete), but the program was still freezing and that's when I remembered the useEffect. For the purposes of the project, I probably should move everything around to be more reasonable, but I found the order of execution really interesting. I would have expected the try catch finally in the click handler to be done after changing the state variable, but it seems like it waits until after the dependent useEffect. Is that correct? Is this related to the funny way that updateState functions don't always work with the latest value so you have to do the updateState(prev=>prev+1) instead of updateState(state+1) thing if its updates twice within a function? Do useEffects insert themselves in the order of execution right after updateState functions in a way that would slow down a finally?
3 Replies
Have you got an example of the code you've written, or something that resembles it? Happy to explain the order things happen in React but probably easier to explain in the context of what you're working with.
Thanks! Here is the function that runs on click:
The setTimeout is a hacky fix to me not understanding promises all the way yet (if I don't have it , the loading spinners never update; my guess is that it forces the next frame to load so the previous setLoading(true) function happens). updateSettings updates a context where I store the current settings and also the input svgs. In a different file, I have a useEffect triggered by settings which is running before loading is turned to false (in the finally of this function)
Just realized that the Hello does run before the useEffect. So my current theory is that the setTimeout forces a rerender, so my initial setLoading(true) (in the button onClick since I thought that might help, though now see it probably doesn't matter) is able to update loading, then the rest of the handleAddSVG happens and settings is updated, then the useEffect runs and then when the frame is updated the setLoading at the bottom takes effect, but since the useEffect lags everything that is a few seconds. Ideally, I'd get the code in the useEffect to run not on the main thread so that the ui doesn't freeze up, but then I'd have to make sure to setLoading somewhere else since if that was pushed off the main thread, the setLoading would run too soon.