How to re-render after each setState in React 18?

209 Views Asked by At

Currently i am facing the following problem:

in my react app, i am executing a wasm by the click of a button. i.e.

const handleExecuteCell = async (cell: CellModel) => {
    execute(cell.code, cell.type, null, cell.stdOut, cell.stdErr);
}

this starts a process, which wants to log some information about the current status. Therefore, i pass the parameters cell.stdOut and stdErr to it, which are callback functions, that should append a line to an output-field at each call. The implementation of the callbacks look like the following:

cell.stdOut = (ln: string) => {
    setOut((prevOut: string) => prevOut == "" ? ln : prevOut + "\n" + ln);
}

(similar for stdErr)

i am using the functional state-update to ensure, that i am appending the line. (without that i would overwrite the state when calling again, because the state doesnt change immediately after)

as the wasm is basically a loop, it would be the same as calling something like:

cell.stdOut("stage one - busy...")
cell.stdOut("stage one - complete!")
cell.stdOut("stage two - busy...")
cell.stdOut("stage two - complete!")

when calling cell.stdOut many times, it does only re-render after the last call (but the state changes correctly). (i.e. when putting many stdOut's in a loop, i have to wait a few seconds, and then suddenly all of them appear at once on my screen) After googling a bit, i found out that React v18 implements automatic batching, which could be the problem.

i have tried by wrapping the content of stdOut in flushSync, like that:

cell.stdOut = (ln: string) => {
    flushSync(() => {
        setOut((prevOut: string) => prevOut == "" ? ln : prevOut + "\n" + ln);
    });
}

but with no luck.

by accident, i figured out that the following code works as intended:

cell.stdOut = async (ln: string) => {
    setOut((prevOut: string) => prevOut == "" ? ln : prevOut + "\n" + ln);
    return new Promise((r) => { window.setTimeout(r, 0) });
}

and then call stdOut like:

await cell.stdOut("stage one - busy...")

but, this seems to be a dirty hack, and i am not sure if that won't cause some troubles in future...

As the mentioned wasm is not ready yet, i need to simulate output with (many) subsequent/looped stdOut calls. (as shown) Does it maybe behave differently when calling stdOut only in my wasm? Or am i missing something? I am pretty new to react, and i was wandered why i couldn't find something, as this seems not to be a very rare case...

0

There are 0 best solutions below