Here’s a comprehensive list of the different types of state management in React, along with their common use cases and real-life project examples where they are frequently used.
1. useState
Description: Basic hook for managing local state within functional components.
- Common Use Case:
- Form input management.
- Simple UI toggles (e.g., show/hide modal).
- Counters and tracking simple values.
Real-Life Examples:
- Form Handling: Capture user input for a login form.
- Modal Visibility: Toggling between showing and hiding a modal dialog in an e-commerce checkout flow.
- Counter: Like a “like” button count on a social media app.
const [inputValue, setInputValue] = useState('');
const [isModalOpen, setModalOpen] = useState(false);
const [counter, setCounter] = useState(0);2. useReducer
Description: A more advanced hook that is ideal for managing complex state logic, similar to Redux but at the component level.
- Common Use Case:
- Managing complex form state.
- Handling multiple state transitions.
- Centralizing state logic (e.g., multi-step forms, shopping cart logic).
Real-Life Examples:
- Multi-Step Forms: Tracking form completion stages.
- Shopping Cart: Managing cart items in an e-commerce store.
- Stateful Wizards: Handling state for a sign-up flow that involves multiple steps like input validation and submission.
const [state, dispatch] = useReducer(reducer, initialState);3. useEffect
Description: Used for handling side effects, like fetching data, listening to events, or manipulating the DOM after rendering.
- Common Use Case:
- Fetching data from an API.
- Subscribing to WebSocket updates.
- Updating the document title dynamically.
Real-Life Examples:
- API Requests: Fetching posts from a blog or product details from an API on component mount.
- Event Listeners: Attaching window resize or scroll event listeners to adjust UI based on user actions.
- Authentication Status: Checking user login status when the app initializes.
useEffect(() => {
fetchData(); // API call on mount
}, []);4. useContext
Description: Used to pass data deeply throughout the component tree without prop drilling.
- Common Use Case:
- Theme management.
- User authentication status.
- Sharing global configuration across components.
Real-Life Examples:
- Theme Switcher: Managing light/dark mode across the entire app.
- User Login State: Sharing authentication status (logged in/out) between different components.
- Language Settings: Managing multi-language support across an application without needing to pass props.
const { user, setUser } = useContext(UserContext);5. useMemo
Description: Memoizes the result of a calculation to avoid expensive recalculations on every render.
- Common Use Case:
- Expensive calculations that should only be recalculated when dependencies change.
- Optimizing performance by preventing unnecessary re-rendering.
Real-Life Examples:
- Filtering Data: Memoizing a filtered list of products or blog posts to avoid recalculating on every render.
- Sorting Large Data Sets: Sorting a large array of data, only recalculating when the sorting criteria change.
const sortedList = useMemo(() => sortData(data), [data]);6. useCallback
Description: Memoizes functions so they are not recreated unnecessarily on every render.
- Common Use Case:
- Passing callback functions to child components.
- Avoiding unnecessary renders when passing functions as props.
Real-Life Examples:
- Performance Optimization: When passing functions to deeply nested components, use
useCallbackto prevent the child from re-rendering unnecessarily. - Event Handlers: Handling form submissions, button clicks, etc., ensuring that the handler function reference stays the same between renders.
const handleClick = useCallback(() => {
console.log('Clicked!');
}, []);7. useRef
Description: Used for persisting values between renders without causing a re-render, and for accessing DOM elements directly.
- Common Use Case:
- Accessing and manipulating DOM elements (e.g., focusing on an input field).
- Storing mutable values like timers or intervals.
- Keeping values that don’t need to trigger a re-render.
Real-Life Examples:
- Focus Input: Automatically focusing an input field when a modal is opened.
- Tracking Timers: Creating and clearing intervals without re-triggering effects.
- Storing Previous Values: Keeping track of the previous value of a state variable.
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);8. useLayoutEffect
Description: Similar to useEffect, but fires synchronously after all DOM mutations. Use this when you need to perform an effect that affects the layout directly.
- Common Use Case:
- Calculating layout dimensions.
- Measuring DOM elements before rendering the final layout.
Real-Life Examples:
- Measuring Elements: Calculating the size or position of DOM elements for animations.
- Synchronizing Layout Changes: Applying CSS transitions or visual updates based on measured DOM properties.
useLayoutEffect(() => {
const height = divRef.current.offsetHeight;
}, []);9. useImperativeHandle
Description: Customizes the instance value that is exposed to parent components when using ref in function components.
- Common Use Case:
- Creating custom refs to expose specific functions or values to parent components.
- Providing a controlled interface for interacting with child components.
Real-Life Examples:
- Custom Input Component: Creating a custom input component that exposes focus methods to the parent.
- Custom Slider/Carousel: Exposing control methods (next, previous) of a custom slider component.
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));10. Global State with Third-Party Libraries (e.g., Redux, Zustand)
Description: For managing global state across larger applications, you can use external libraries like Redux or Zustand. These help manage state that is needed throughout your entire application, with powerful features like middleware and time travel debugging.
- Common Use Case:
- Complex apps with global state (e.g., authentication, user settings).
- Managing shared data like shopping carts, user preferences, or product listings.
Real-Life Examples:
- E-Commerce Cart: Managing cart items and order information across different components.
- User Session: Storing user authentication status and profile data globally.
- Complex Forms: Managing large forms where fields and data must be globally accessible.
const store = configureStore({
reducer: rootReducer,
});Choosing the Right State in Projects:
- Local Component State (
useState): Best for managing individual component states like input fields, UI toggles, and counters. - Complex State Management (
useReducer): Ideal for components with more complex logic, like multi-step forms or nested UI interactions. - Shared State Across Components (
useContext): Useful when you need to share state like themes or user information across different components without prop drilling. - Performance Optimization (
useMemo,useCallback): When optimizing performance, especially for preventing unnecessary re-renders or expensive calculations. - DOM Manipulation (
useRef): Perfect for directly interacting with DOM elements (e.g., focusing input fields) or storing mutable values that don’t trigger re-renders.
Understanding when and how to use each of these hooks or state management strategies will allow you to make informed decisions and optimize your application’s performance and maintainability.
Leave a Reply