STATE
Managing State and Side Effects in React
React has revolutionized front-end development by introducing hooks that simplify managing state and side effects. For software engineers, business leaders, or students with IT knowledge, mastering these hooks — useState, useReducer, useEffect, and useLayoutEffect — is crucial for building scalable, maintainable, and performant React applications.
INTRODUCTION
Introduction to React State Management and Side Effects
React components are the building blocks of UI, and their ability to manage state effectively is key to a seamless user experience. State refers to data that determines the component’s behavior and rendering. Side effects include operations like data fetching, subscriptions, or manually manipulating the DOM that occur after rendering.
React provides hooks — functions that let you use React state and lifecycle features in functional components. Four foundational hooks are:
React provides hooks — functions that let you use React state and lifecycle features in functional components. Four foundational hooks are:
- useState: local state management
- useReducer: complex state logic
- useEffect: asynchronous side effects
- useLayoutEffect: synchronous DOM-related effects
Local State with useState
useState is the simplest hook for managing local state in functional components. It returns a state variable and a function to update it. 1
Use Cases
Use Cases
- Handle user login form inputs
- Toggle UI elements like modals or dropdown menus
- Manage simple counters or timers
- Control loading or error flags for API calls
- Store and update transient UI state like tab selections
Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { useState } from "react";
function App() {
// state
const [favorite, setFavorite] = useState("JavaScript");
return (
<>
<h1>My favorite language is {favorite}</h1>
<h6>Click the button to change favorite</h6>
<button onClick={() => setFavorite("Python")}>Python</button>
<button onClick={() => setFavorite("PHP")}>PHP</button>
<button onClick={() => setFavorite("Java")}>Java</button>
<button onClick={() => setFavorite("C#")}>C#</button>
</>
);
}
export default App;
Complex State Logic Using useReducer
useReducer is preferred when state logic is complex or depends on previous states. It allows you to centralize state updates through a reducer function, similar to Redux but scoped to a component.
2
Use Cases
Use Cases
- Manage complex form states with multiple fields
- Handle multi-step workflows or wizards with varied user actions
- Implement undo/redo functionality in editors and apps
- Coordinate state transitions based on previous state or dispatched actions
- Manage state logic that requires grouping or combining multiple updates
Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { useReducer } from "react";
// initial
const intialState = {
count: 0,
};
// types
type State = typeof intialState;
type Action = {
type: "increment" | "decrement";
};
/**
* This is a reducer function that takes in a state and an action object
* and returns a new state based on the action type.
*/
function reducer(state: State, action: Action) {
switch (action.type) {
case "increment":
return { count: state.count + 1 };
case "decrement":
return { count: state.count - 1 };
default:
return state;
}
}
function App() {
// states
const [state, dispatch] = useReducer(reducer, intialState);
return (
<>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({ type: "increment" })}>Increment</button>
<button onClick={() => dispatch({ type: "decrement" })}>Decrement</button>
</>
);
}
export default App;
Managing Side Effects with useEffect
Side effects happen outside React’s rendering, such as fetching data, subscriptions, or timers. `useEffect` allows you to perform these asynchronously after the component renders. Avoid pitfalls like missing dependencies or creating infinite loops
3
Use Cases
Use Cases
- Fetch data on component mount or when dependencies change
- Subscribe to and clean up event listeners (e.g., window resize)
- Manage timers and intervals for animations or repeats
- Update document title or browser APIs based on state changes
- Clean up resources like websocket connections or abort fetches
Basic Syntax
Copy to clipboard
1
2
3
4
5
6
7
8
9
// import { useState } from "react";
useEffect(() => {
// effect logic here
return () => {
// optional cleanup
};
}, [dependencies]);
- Executes after rendering and whenever dependencies change
- Cleanup function runs before re-execution or unmounting
Example: Data Fetching
Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { useEffect, useState } from "react";
function App() {
// states
const [data, setData] = useState<string[]>([]);
useEffect(() => {
// In this example, useEffect is used to fetch data from an API.
// The effect runs once, after the initial render, and fetches data from a hypothetical API that returns an array of strings.
// The fetched data is stored in the data state variable using the setData function.
setTimeout(() => {
const newData = ["a", "b", "c", "d"];
setData(newData);
}, 3000);
}, []);
return (
<>
<p>
{data?.length > 0
? data.map((item: any) => <p>{item}</p>)
: "loading..."}
</p>
</>
);
}
export default App;
Explore project snapshots or discuss custom solutions.
ASYNC
Synchronous Effects with useLayoutEffect
useLayoutEffect is similar to useEffect but runs synchronously after all DOM mutations but before the browser paints. It’s ideal for measuring or manipulating layout to prevent flickers.
4
Use Cases
Use Cases
- Measure and synchronize DOM element sizes before paint
- Adjust scroll positions or manage focus synchronously
- Prevent visual flickering during layout recalculations or animations
- Synchronize animations that depend on DOM measurements
- Modify or prepare DOM in ways that must happen before user's view update
Use useLayoutEffect when you must read layout or synchronously re-render before the user sees anything
Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { useLayoutEffect, useRef, useState } from "react";
function App() {
// states
const ref = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
// useLayoutEffect is used to update the width of the div
useLayoutEffect(() => {
setWidth(ref.current?.getBoundingClientRect().width || 0);
}, [ref]);
return (
<>
<div ref={ref}>Width: {width}px</div>
</>
);
}
export default App;
Common Pitfalls
Mastering React hooks for state and side effects empowers you to build robust, efficient, and clean applications whether you are a developer, manager, or entrepreneur.
- Infinite loops by missing dependencies in useEffect
- Overusing useLayoutEffect causing performance hits
- Mutating state directly instead of immutably updating
- Neglecting cleanup in effects leading to memory leaks
Comparison
| Hook | Purpose | Use Case | Characteristics |
|---|---|---|---|
| useState | Declares a state variable | Simple, independent state management | Easy for primitive or small state. Directly update state variable. |
| useReducer | Declares state with reducer function | Complex state logic, multiple related state values | Better for complex transitions, ties multiple properties, uses dispatch. |
| useEffect | Handles side effects | Data fetching, subscriptions, effects after render | Runs after render. Dependency array for controlled execution. |
| useLayoutEffect | Synchronous effect before paint | Measure DOM, sync mutations before painting | For DOM measurements. Blocks paint until effect finishes, runs earlier. |
The best performance improvement is the transition from the nonworking state to the working state.
John Ousterhout
A Philosophy of Software Design
Thank You for Spending Your Valuable Time
I truly appreciate you taking the time to read blog. Your valuable time means a lot to me, and I hope you found the content insightful and engaging!
FAQ's
Frequently Asked Questions
Use useReducer for complex state with multiple sub-values or when the next state depends on the previous one
This often happens due to missing or incorrect dependencies, or React Strict Mode running effects twice in development, But live version only one time.
No, useLayoutEffect runs synchronously and accesses the DOM, which is not available during server-side rendering.
A cleanup function releases resources or cancels subscriptions to prevent memory leaks when a component unmounts or before the effect re-runs.
Always specify dependency arrays explicitly, and don't update state unnecessarily within the effect.
- Available at: https://reactjs.org/docs/hooks-state.html
- Available at: https://reactjs.org/docs/hooks-reference.html#usereducer
- Available at: https://reactjs.org/docs/hooks-effect.html
- Available at: https://reactjs.org/docs/hooks-reference.html#uselayouteffect
Comments are closed