Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

When callback in useEffect hook fires?

I’ve read, that useEffect is asynchronous, so React firstly renders and mounts page and only after callback in useEffect fires, so sometimes we see value changing really fast in some components.

But when I tried to use self-created "sleep-for-3-seconds" in useEffect callback, React waits until 3 seconds pass, and only after that renders and mounts page, so I see blank page for 3 seconds. Also we can notice fast-changing value after 3 seconds delay. I don’t get why it happens, as useEffect callback should be run after rendering and mounting the page.

Can you please explain why it happens in a such way?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Code Sandbox: Link

Code:

import { useEffect, useState } from "react";
import "./styles.css";

export default function App() {
  const [state, setState] = useState(1200000000);
  useEffect(() => {
    let start = new Date().getTime();
    let end = start;
    while (end < start + 3000) {
      end = new Date().getTime();
    }
    setState(3);
  }, []);
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <div>Value is : {state}</div>
    </div>
  );
}

>Solution :

I’ve read, that useEffect is asynchronous, so React firstly renders and mounts page and only after callback in useEffect fires, so sometimes we see value changing really fast in some components.

Correct. That’s documented here:

The function passed to useEffect will run after the render is committed to the screen.

and here

Timing of effects

Unlike componentDidMount and componentDidUpdate, the function passed to useEffect fires after layout and paint, during a deferred event.

(their emphasis)

Continuing with your question:

But when I tried to use self-created "sleep-for-3-seconds" in useEffect callback, React waits until 3 seconds pass, and only after that renders and mounts page, so I see blank page for 3 seconds.

That’s odd, I don’t see that behavior:

const { useState, useEffect} = React;

function App() {
    const [state, setState] = useState(1200000000);
    useEffect(() => {
        let start = new Date().getTime();
        let end = start;
        while (end < start + 3000) {
            end = new Date().getTime();
        }
        setState(3);
    }, []);
    return (
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
        <div>Value is : {state}</div>
      </div>
    );
}

ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

When I run that, I see the page rendered with the initial 1200000000 state value, then three seconds later I see it updated with the modified state value.

I also don’t see it with this simpler example:

const {useEffect} = React;

const Example = () => {
    useEffect(() => {
        const stop = Date.now() + 3000;
        while (Date.now() < stop) {
            // Wait (NEVER DO THIS IN REAL CODE)
        }
        console.log("Done waiting");
    }, []);
    return <div>x</div>;
};

ReactDOM.render(<Example />, document.getElementById("root"));
<div id="root"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>

When I run that, I see the x rendered by the component, then three seconds later I see the console.log that the wait has finished.

But if you aren’t seeing what you describe, it would be just because the browser hasn’t had a chance to actually draw the page before the useEffect is called, despite React’s code trying to ensure that’s the case. And since your code is a busy-wait, the main thread is tied up with that loop, and the browser can’t update the page display.

But again, I don’t see that with useEffect (you would with useLayoutEffect).

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading