React Hooks
Definition: A hook used to add state to a functional component. Returns an array containing the current value and a setter function to update that value.
On this page
React Hooks
1. useState
Definition: A hook used to add state to a functional component. Returns an array containing the current value and a setter function to update that value.
Example:
const [email, setEmail] = useState("");
const [isLoading, setIsLoading] = useState(false);
const handleLogin = async () => {
setIsLoading(true);
setIsLoading(false);
};
2. useEffect
Definition: A hook for performing side effects in a functional component. Runs after the component renders and can clean up when the component unmounts.
Use cases:
- Fetching data from an API when the component mounts
- Subscription/cleanup (WebSocket, timers, event listeners)
- Updating the document title
- Reacting to changes in props/state
Example:
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
if (userId) {
fetch(`/api/users/${userId}`)
.then((res) => res.json())
.then(setUser);
}
}, [userId]);
}
How the dependency array affects useEffect
-
Empty dependency array (
[]):- The effect runs once after the initial render and its cleanup runs once on unmount.
- This is roughly analogous to
componentDidMountpluscomponentWillUnmount, but with an important caveat: in development with Strict Mode, React intentionally mounts, unmounts, and remounts each component to surface bugs in effect cleanup. Your effect (and its cleanup) will run twice on the initial mount in development. Production runs the effect once.
javascriptuseEffect(() => { // This code runs after the initial render return () => { // Cleanup runs on unmount }; }, []); -
Dependency array with variables:
- The effect runs after the initial render and whenever any of the specified dependencies change.
- React compares each dependency with its previous value using
Object.is(reference equality, not a deep or shallow object comparison). Inline objects, arrays, or functions therefore change identity on every render unless memoized.
javascriptuseEffect(() => { // This code runs after the initial render and whenever dependency1 or dependency2 changes }, [dependency1, dependency2]); -
No dependency array:
- The effect runs after every render.
- This can lead to performance issues if the effect is expensive.
javascriptuseEffect(() => { // This code runs after every render });
Cleanup behavior on dependency change
When dependencies change, React first runs the previous effect's cleanup function (if any) before running the effect again with the new values. The same is true on unmount. This means dependency changes effectively perform a tear-down/set-up cycle, which matters for subscriptions, intervals, and event listeners.
useEffect(() => {
const subscription = source.subscribe(id);
return () => subscription.unsubscribe(); // runs before next effect or on unmount
}, [id]);
3. useContext
Definition: A hook for accessing a value from React Context without using Context.Consumer, allowing you to share global data between components.
Use cases:
- Managing global authentication state
- Theme switching (dark/light mode)
- Multi-language (i18n)
- Shopping cart data
- User preferences
Example:
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<Content />
</ThemeContext.Provider>
);
}
function Header() {
const { theme, setTheme } = useContext(ThemeContext);
return (
...
);
}
4. useRef
Definition: A hook that creates a ref object with a .current property, used to reference a DOM element or store a mutable value without triggering a re-render.
Use cases:
- Focusing input elements
- Scrolling to a specific position
- Storing timer IDs, previous values
- Integrating with external libraries (DOM manipulation)
- Measuring an element's size
Example:
function SearchInput() {
const inputRef = useRef();
const countRef = useRef(0);
const focusInput = () => {
inputRef.current.focus();
};
const handleSearch = () => {
countRef.current += 1;
// countRef changes but doesn't trigger a re-render
};
return (
<div>
<input ref={inputRef} placeholder="Search..." />
<button onClick={focusInput}>Focus Input</button>
<button onClick={handleSearch}>Search</button>
</div>
);
}
5. useMemo
Definition: A hook for memoizing (caching) the result of an expensive computation, only recalculating when the dependencies change, which helps optimize performance.
Use cases:
- Filtering/sorting large lists
- Complex calculations (mathematical operations)
- Transforming data for charts/graphs
- Expensive object/array operations
- Formatting displayed data
Example:
const Child = ({ data }) => {};
const ChildMemo = React.memo(Child);
function ProductList({ products, searchTerm, sortBy }) {
const filteredProducts = useMemo(() => {
return products
.filter((p) => p.name.toLowerCase().includes(searchTerm.toLowerCase()))
.sort((a, b) => a[sortBy] - b[sortBy]);
}, [products, searchTerm, sortBy]);
return <ChildMemo data={filteredProducts} />;
}
6. useCallback
Definition: A hook for memoizing a function, avoiding creating a new function instance on every render - especially useful when passing a function as a prop to a memoized child component.
Use cases:
- Event handlers for React.memo components
- Function dependencies in useEffect
- Preventing unnecessary re-renders of child components
- Callback functions for third-party libraries
Example:
const AddTodoForm = ({ onAdd }) => {};
const AddTodoFormMemo = React.memo(AddTodoForm);
function TodoApp() {
const [todos, setTodos] = useState([]);
const addTodo = useCallback((text) => {
setTodos((prev) => [...prev, { id: Date.now(), text, completed: false }]);
}, []);
return (
<div>
<AddTodoFormMemo onAdd={addTodo} />
</div>
);
}
7. useReducer
Definition: A hook for managing complex state through a reducer function, similar to the Redux pattern. It takes a reducer function and an initial state, and returns the current state and a dispatch function.
Use cases:
- Complex forms with many fields and validation
- Shopping cart with multiple actions
- Game state management
- Complex UI state (modal, wizard, multi-step form)
- State with complex update logic
Example:
const initialState = { items: [], total: 0 };
function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM':
const newItems = [...state.items, action.item];
return {
items: newItems,
total: newItems.reduce((sum, item) => sum + item.price * item.quantity, 0)
};
case 'REMOVE_ITEM':
const filteredItems = state.items.filter(item => item.id !== action.id);
return {
items: filteredItems,
total: filteredItems.reduce((sum, item) => sum + item.price * item.quantity, 0)
};
case 'CLEAR_CART':
return initialState;
default:
return state;
}
}
function ShoppingCart() {
const [cart, dispatch] = useReducer(cartReducer, initialState);
const addItem = (item) => {
dispatch({ type: 'ADD_ITEM', item });
};
const removeItem = (id) => {
dispatch({ type: 'REMOVE_ITEM', id });
};
return (
...
);
}
Summary - when to use which hook
| Situation | Suitable hook | Reason |
|---|---|---|
| Simple form input | useState | Basic state management |
| Calling an API on mount | useEffect | Side effect on component initialization |
| Sharing global data | useContext | Avoids prop drilling |
| Focusing an input, scrolling | useRef | Interacting with the DOM |
| Filtering a large list | useMemo | Optimizing expensive computation |
| Props for a memoized component | useCallback | Avoiding unnecessary re-renders |
| Complex state logic | useReducer | Managing state with many actions |
8. useTransition()
The useTransition() hook is a powerful tool introduced in React 18 for Concurrency Control. It allows you to mark certain state updates as "transitions," telling React that they are not urgent and can be interrupted by more important tasks.
By default, all state updates in React are urgent. If an update takes a long time to render (like filtering a massive list), the entire browser UI becomes unresponsive until that work is finished. This creates "jank" or a frozen feeling for the user.
useTransition lets you split your UI updates into two categories:
- Urgent updates: Changes that need immediate feedback (like typing in an input field).
- Transition updates: Changes that can take a moment to appear (like updating a list of search results).
The Syntax
const [isPending, startTransition] = useTransition();
isPending: A boolean that istruewhile the transition is happening. You can use this to show a loading spinner or dim the UI.startTransition: A function that wraps the state update you want to mark as low priority.
3. Practical Example: Search Filtering
Imagine a search bar where typing updates the input field (urgent) and also filters a list of 10,000 items (not urgent).
import { useState, useTransition } from "react";
function SearchComponent() {
const [query, setQuery] = useState("");
const [list, setList] = useState(bigData);
const [isPending, startTransition] = useTransition();
function handleChange(e) {
// 1. Urgent: Update the input field immediately
setQuery(e.target.value);
// 2. Non-Urgent: Mark the expensive filtering as a transition
startTransition(() => {
const filtered = bigData.filter((item) => item.includes(e.target.value));
setList(filtered);
});
}
return (
<div>
<input type="text" value={query} onChange={handleChange} />
{isPending && <p>Updating list...</p>}
<div style={{ opacity: isPending ? 0.5 : 1 }}>
{list.map((item) => (
<ListItem key={item} name={item} />
))}
</div>
</div>
);
}
Note: This document was created by @Tranloi2k on 2025-09-22. The examples are designed to be concise and practical for easy understanding and application.
