Home > Blockchain >  With flushSync() inside useEffect do we do the same as using useLayoutEffect?
With flushSync() inside useEffect do we do the same as using useLayoutEffect?

Time:02-02

https://reactjs.org/docs/react-dom.html#flushsync

Force React to flush any updates inside the provided callback synchronously. This ensures that the DOM is updated immediately.

// Force this state update to be synchronous.
flushSync(() => {
  setCount(count   1);
});
// By this point, DOM is updated.

Knowing that, it is the same as using useLayoutEffect, or do I misunderstand flushSync()?

const App = () => {
    const [name, setName] = React.useState("Leonardo");
    React.useEffect(() => {
        ReactDOM.flushSync(() => {
            for (let index = 1; index <= 100000; index  ) { // for simulate blocking
                console.log(index);
            }
            setName("Jose");
        });
    });
    return (
        <div>
            <h1>Hello {name}</h1>
        </div>
    );
};

¿it is the same that this?

React.useLayoutEffect(() => {
  for (let index = 1; index <= 100000; index  ) {
   console.log(index);
  }
  setName("Jose");
});

CodePudding user response:

flushSync is used to force React to flush a state update and when you try to put it inside useEffect it won't affect when useEffect is invoked, it will always be after the changes have been reflected on the browser, whereas useLayoutEffect is invoked before and this is the main difference between them.

so flushSync is not a function that is supposed to be executed inside useEffect you will even get this warning

Warning: flushSync was called from inside a lifecycle method. React cannot flush when React is already rendered. Consider moving this call to a scheduler task or microtask.

CodePudding user response:

useLayoutEffect is useful for things that need to happen before paint your dom or when your code is causing flickering. it's already synchronous and executed always before every useEffect hook in your code.

flushSync is used to convert the setState into synchronous. in 99% of cases you will use flushSync inside a handler like a form submit handler, outside of useEffect to execute an imperative action

function handleSubmit(values) {
  flushSync(() => {
   setForm(values);
  });
}

Be aware that flushSync force a re-rendering, so use it carefully

The common use case of flushSync is update the DOM after settings the state immediately. example scroll to the new added element in the list

 flushSync(() => {
   setElements((elements) => [
    ...elements,
    {
      id: 'random',
    },
   ]);
 });
 // scroll to element here

Check this example https://codesandbox.io/s/react-18-batching-updates-flushsync-forked-vlrbq8. you can delete flushSync and see the diff

CodePudding user response:

They are not same. Your code might give the same performance, functional, and results, yet they are different in nature. flushSync is a low-level API that flushes updates to the React DOM immediately, bypassing the normal scheduling mechanism. It should be used sparingly and only when necessary, as it can lead to poor performance and is more prone to bugs and inconsistencies. On the other hand, useLayoutEffect is a higher-level hook that schedules a DOM update to happen synchronously after all other updates have been processed. It is generally the preferred way to ensure that updates are synchronized with the layout of the page.

CodePudding user response:

I did this code to see if I understood everything, please correct me:

function App2() {
    const [c, setC] = React.useState(0);
    const inc1 = () => {

      /*setC is asynchronous (withoutFlushSync), so it continues the 
      code downwards and therefore from the DOM it 
      brings us the value without increment in 1*/
      setC((c) => c   1);

      console.log(document.getElementById("myId").innerText); // old value from DOM
      console.log(c); // However below log will still point to old value ***BECAUSE OF CLOSURE***
    };

    const inc2 = () => {
      /*waits until the DOM is modified with the new state (in the first click
      c = 1)*/
      ReactDOM.flushSync(() => { // Wait
        setC((c) => c   1);
      });

      /* brings the c = 1 of the DOM because it waited until the DOM 
      was modified with the new state incremented by one. */
      console.log(document.getElementById("myId").innerText); // new value from DOM 

      console.log(c); // However below log will still point to old value ***BECAUSE OF CLOSURE***
    };
    return (
      <div className="App">
        Count: <div id="myId">{c}</div>
        <button onClick={inc1}>without flushSync</button>
        <button onClick={inc2}>with flushSync</button>
      </div>
    );
  }
  • Related