Frontend Development 12 min read

Understanding the Execution Timing of useEffect in React

This article explains when React's useEffect callbacks run—whether synchronously before rendering or asynchronously after rendering—by analyzing four demos, discussing the impact of event types, rendering workload, and React's internal scheduling rules.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Understanding the Execution Timing of useEffect in React

Introduction

Many React developers assume that the useEffect callback is always invoked asynchronously after the browser finishes rendering, but this is not always true. The actual execution timing can be either before or after rendering depending on various factors.

Demo 1: useEffect Executed Before Rendering

A simple component logs numbers from render, useEffect, a micro‑task (Promise), and a macro‑task (setTimeout). The observed console order is 1, 2, 3, 4 , showing that useEffect runs synchronously before the browser paints.

import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [state] = useState(0);
  console.log(1);
  useEffect(() => { console.log(2); }, [state]);
  Promise.resolve().then(() => console.log(3));
  setTimeout(() => console.log(4), 0);
  return
useEffect execution timing
;
}

export default App;

Demo 2: useEffect Executed After Rendering

A 100 ms blocking while loop is added to the render function. The console order becomes 1, 3, 4, 2 , confirming that useEffect is deferred until after the paint when rendering takes noticeable time.

import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [state] = useState(0);
  console.log(1);
  const flag = Date.now();
  while (Date.now() - flag < 100) {}
  useEffect(() => { console.log(2); }, [state]);
  Promise.resolve().then(() => console.log(3));
  setTimeout(() => console.log(4), 0);
  return
When is useEffect executed?
;
}

export default App;

useEffect in User Events

Demo 3: Click Event – useEffect Runs Synchronously

A button updates state on click. With the same 100 ms render block, the console order is 1, 2, 3, 4 , meaning the effect runs before the paint because the click is a discrete event.

import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [state, setState] = useState(0);
  console.log(1);
  const flag = Date.now();
  while (Date.now() - flag < 100) {}
  useEffect(() => { console.log(2); }, [state]);
  Promise.resolve().then(() => console.log(3));
  setTimeout(() => console.log(4), 0);
  return (
setState(s => s + 1)}>Click to update state
);
}

export default App;

Demo 4: onMouseEnter – useEffect Runs Asynchronously

Changing the event to onMouseEnter (a continuous event) yields the order 1, 3, 4, 2 . Here React treats the event as non‑discrete, so the effect is deferred until after the paint, similar to non‑interactive cases.

import { useEffect, useState } from 'react';
import './App.css';

function App() {
  const [state, setState] = useState(0);
  console.log(1);
  const flag = Date.now();
  while (Date.now() - flag < 1000) {}
  useEffect(() => { console.log(2); }, [state]);
  Promise.resolve().then(() => console.log(3));
  setTimeout(() => console.log(4), 0);
  return (
setState(10)}>Hover to update State {state}
);
}

export default App;

Summary of useEffect Timing

React does not guarantee a fixed position for useEffect execution. For non‑interactive effects, React may run the callback synchronously before paint if there is remaining work time, otherwise it defers it asynchronously. For discrete user events (click, key press), the effect runs synchronously before paint; for continuous events (mouse move, hover), the same rule as non‑interactive effects applies.

Understanding these rules helps developers avoid unexpected bugs related to state updates and UI rendering.

Conclusion

Although the React team prefers developers not to rely on these internal details, knowing when useEffect actually fires can clarify confusing behavior and lead to more predictable component logic.

frontendJavaScriptreactHooksEvent LoopuseEffect
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.