
React Hooks revolutionized the way developers manage state and side effects in functional components. While useState
and useEffect
are commonly used, advanced hooks like useReducer
, useRef
, useMemo
, and useCallback
provide deeper control over state management, performance optimization, and DOM manipulation. Additionally, creating custom hooks allows us to reuse logic efficiently. In this blog, we will explore these advanced hooks in detail.
1. useReducer – Alternative to useState
useReducer
is a powerful hook that provides an alternative to useState
, especially when dealing with complex state logic. It follows a reducer pattern similar to Redux, where actions dictate state changes.
Syntax:
const [state, dispatch] = useReducer(reducer, initialState);
Example:
import React, { useReducer } from "react"; const initialState = { count: 0 }; const reducer = (state, action) => { switch (action.type) { case "increment": return { count: state.count + 1 }; case "decrement": return { count: state.count - 1 }; default: return state; } }; function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <div> <p>Count: {state.count}</p> <button onClick={() => dispatch({ type: "increment" })}>+</button> <button onClick={() => dispatch({ type: "decrement" })}>-</button> </div> ); } export default Counter;
When to Use useReducer
?
- When state transitions depend on previous state.
- When dealing with multiple sub-values in state.
- When state logic is complex and involves multiple actions.
2. useRef – Accessing DOM Elements
useRef
allows direct interaction with DOM elements and persists values across renders without causing re-renders.
Example:
import React, { useRef } from "react"; function InputFocus() { const inputRef = useRef(null); const focusInput = () => { inputRef.current.focus(); }; return ( <div> <input ref={inputRef} type="text" placeholder="Type here..." /> <button onClick={focusInput}>Focus Input</button> </div> ); } export default InputFocus;
When to Use useRef
?
- Accessing and manipulating DOM elements.
- Storing values that should persist across renders without causing re-renders.
- Holding references to timers or event listeners.
3. useMemo – Performance Optimization
useMemo
memoizes a value, preventing expensive recalculations when dependencies have not changed.
Example:
import React, { useState, useMemo } from "react"; function ExpensiveComputation({ num }) { const expensiveValue = useMemo(() => { console.log("Computing..."); return num * 10; }, [num]); return <p>Computed Value: {expensiveValue}</p>; } function App() { const [count, setCount] = useState(0); return ( <div> <ExpensiveComputation num={count} /> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default App;
When to Use useMemo
?
- To optimize performance when computing expensive values.
- Avoid unnecessary recalculations in functional components.
4. useCallback – Preventing Unnecessary Re-renders
useCallback
memoizes functions, preventing their recreation unless dependencies change.
Example:
import React, { useState, useCallback } from "react"; function Child({ handleClick }) { console.log("Child component re-rendered"); return <button onClick={handleClick}>Click Me</button>; } function Parent() { const [count, setCount] = useState(0); const memoizedCallback = useCallback(() => { console.log("Button clicked!"); }, []); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> <Child handleClick={memoizedCallback} /> </div> ); } export default Parent;
When to Use useCallback
?
- When passing functions as props to memoized child components.
- To prevent unnecessary re-creations of functions.
5. Creating Custom Hooks
Custom hooks allow you to extract and reuse logic across multiple components.
Example: Custom Hook for Fetching Data
import { useState, useEffect } from "react"; function useFetch(url) { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { fetch(url) .then(response => response.json()) .then(data => { setData(data); setLoading(false); }); }, [url]); return { data, loading }; } export default useFetch;
Using the Custom Hook
import React from "react"; import useFetch from "./useFetch"; function UserList() { const { data, loading } = useFetch("https://jsonplaceholder.typicode.com/users"); if (loading) return <p>Loading...</p>; return ( <ul> {data.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> ); } export default UserList;
When to Use Custom Hooks?
- When logic needs to be reused across multiple components.
- To abstract complex functionality into a separate function.
Conclusion
Advanced React hooks such as useReducer
, useRef
, useMemo
, useCallback
, and custom hooks enable developers to optimize performance, manage state effectively, and reuse logic efficiently. Understanding these hooks is essential for writing scalable and maintainable React applications. Try integrating them into your next project to take full advantage of React’s powerful hooks system!
Leave a Comment