What does flushSync() do in React?

Viewed 13401

I saw Dan Abramov's demo project at JSConf Iceland, but I didn't understand why he used flushSync in this code:

  import { flushSync } from 'react-dom';

  debouncedHandleChange = _.debounce(value => {
    if (this.state.strategy === 'debounced') {
      flushSync(() => {
        this.setState({value: value});
      });
    }
  }, 1000);

What does flushSync do in react?

2 Answers

flushSync flushes the entire tree and actually forces complete re-rendering for updates that happen inside of a call, so you should use it very sparingly. This way it doesn’t break the guarantee of internal consistency between props, state, and refs.

It is not documented properly yet. Read more here https://github.com/facebook/react/issues/11527

Chilarai has a great answer! (Maybe due to time of answer and now having more documentation) I do not agree with the idea of using it very sparingly. As a lot of the time it will stop the need for an extra unneeded useEffect (It is also against how Dan suggests to use useEffect https://twitter.com/dan_abramov/status/1501737272999301121).

Here is a great example of what it achieves based on Dan Abramov's code:

The requirement for this code is an Auto-scroll on new message (I use this code currently and it is very effective).

Before, in this example we are unable to scroll after setMessages as the DOM will not be rendered yet and a hook is needed.

export default function App() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    let socket = connect();

    socket.onMessage((message) => {
      setMessages((m) => [
        ...m,
        message
      ])
    });
    
    return () => {
      socket.disconnect();
    };
  }, [])

  function scrollToLastMessage() {
    // ...
  }

  // NOTE: In order to scroll without using flushSync a unneeded hook
  useEffect(() => {
    scrollToLastMessage();
  }, [messages])
}

After, in this example the DOM is forced to be flush synced and then we can call in the order we wanted.

export default function App() {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    let socket = connect();

    function scrollToLastMessage() {
      // ...
    }

    socket.onMessage((message) => {
      flushSync(() => {
        setMessages((m) => [
          ...m,
          message
        ])
      })
      scrollToLastMessage();
    });
    
    return () => {
      socket.disconnect();
    };
  }, [])
}

This code comes from Dan in this video https://www.youtube.com/watch?v=uqII0AOW1NM at 35:25 I would recommend watching that!

I would also highly recommend adding flushSync to your toolbelt!

Related