DEV Community

Cover image for Mastering React Hooks ๐Ÿช: Dive into `useState`, `useEffect`, and Beyond!
Ashish prajapati
Ashish prajapati

Posted on

Mastering React Hooks ๐Ÿช: Dive into `useState`, `useEffect`, and Beyond!

React Hooks ๐Ÿช have simplified managing state, handling side effects, and sharing data across components. Let's dive into six essential hooks: useState, useEffect, useContext, useReducer, useMemo, and useCallback. ๐Ÿš€


1. useState โ€“ Managing Local State ๐Ÿ—„๏ธ

The useState Hook ๐Ÿ“ฆ lets you add state to functional components, returning a state variable and a function to update it. Itโ€™s ideal for managing simple, local states, such as toggles or counters.

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // ๐Ÿงฎ Initial count is 0

  return (
    <div>
      <p>Count: {count} ๐Ÿ”ข</p>
      <button onClick={() => setCount(count + 1)}>Increment โž•</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • Syntax: const [state, setState] = useState(initialValue);
  • ๐Ÿ›  State Update: Calling setCount re-renders the component with updated state.

2. useEffect โ€“ Handling Side Effects โš™๏ธ

useEffect lets you perform side effects in your components, such as fetching data, setting up subscriptions, or manually changing the DOM. Think of it as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount.

import { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => setSeconds(prev => prev + 1), 1000);
    return () => clearInterval(interval); // ๐Ÿงน Cleanup interval on unmount
  }, []); // Empty array = run only once

  return <p>Seconds: {seconds} โฑ๏ธ</p>;
}
Enter fullscreen mode Exit fullscreen mode
  • Dependency Array: Controls when the effect runs. An empty array ([]) means it runs only once, while leaving it out runs the effect on every render.
  • ๐Ÿงน Cleanup Function: Return a function to clean up, like clearing intervals or removing event listeners.

3. useContext โ€“ Accessing Context ๐ŸŒ

The useContext Hook allows components to subscribe to React Context directly without a wrapper, useful for global data like themes or user info.

import { useContext, createContext } from 'react';

const ThemeContext = createContext('light'); // ๐ŸŒž Default theme

function ThemeToggler() {
  const theme = useContext(ThemeContext); // ๐Ÿ–Œ๏ธ Get the theme

  return <button>{theme === 'light' ? 'Switch to Dark ๐ŸŒ‘' : 'Switch to Light ๐ŸŒž'}</button>;
}
Enter fullscreen mode Exit fullscreen mode
  • ๐ŸŒŽ Global Data: Perfect for app-wide settings or user data.
  • ๐Ÿš€ Simplifies Code: No more Consumer components, just access context with useContext.

4. useReducer โ€“ Complex State Logic ๐Ÿ”„

For more complex state logic, consider useReducer, which is a lightweight version of Redux. Itโ€™s excellent for managing states that depend on multiple actions or values.

import { useReducer } from 'react';

function counterReducer(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(counterReducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count} ๐Ÿ“Š</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increase โž•</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrease โž–</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • Syntax: useReducer(reducer, initialState);
  • ๐Ÿ›  Action Dispatch: Call dispatch to trigger actions and update state.

5. useMemo โ€“ Optimizing Expensive Computations ๐Ÿ’ก

useMemo ๐Ÿง  helps optimize performance by memoizing expensive computations. It returns a memoized value, recalculating only if dependencies change, reducing unnecessary re-renders.

import { useState, useMemo } from 'react';

function ExpensiveCalculationComponent({ num }) {
  const calculate = (n) => {
    console.log("Calculating..."); // See when it actually runs
    return n ** 2; // โšก๏ธ Some intensive computation
  };

  const result = useMemo(() => calculate(num), [num]); // Only re-runs when `num` changes

  return <p>Result: {result} ๐Ÿ“ˆ</p>;
}
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿ•’ Avoids Redundant Work: Only recalculates if num changes.
  • โšก๏ธ Optimizes Components: Useful in lists, large data operations, or nested components.

6. useCallback โ€“ Memoizing Functions ๐Ÿงฉ

useCallback is similar to useMemo, but itโ€™s specifically for memoizing functions. It returns a memoized version of the callback function, ensuring it doesnโ€™t change unless dependencies do.

import { useState, useCallback } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prev => prev + 1); // Only redefined if dependencies change
  }, []);

  return (
    <div>
      <p>Count: {count} ๐Ÿ”ข</p>
      <button onClick={increment}>Increment โž•</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • ๐Ÿงฉ Dependency Management: Prevents function re-creation on every render.
  • ๐Ÿš€ Improves Performance: Particularly useful in components with frequent re-renders or passing functions as props.

Final Thoughts ๐ŸŒŸ

Hooks like useState, useEffect, useContext, useReducer, useMemo, and useCallback have transformed how we build React apps. By using them wisely, you can keep your code clean, optimized, and maintainable!

Top comments (5)

Collapse
 
said_aazri_7660bae02b8085 profile image
Said AAZRI

Good job, thank you, bro!

Collapse
 
tymzap profile image
Tymek Zapaล‚a

I prefer to abstract useContext usages behind higher level hook, for example when accessing user context it would be:

function useUser() {
   const user = useContext(UserContext);

  if (!user) {
    throw new Error('user not accessible');
  }

  return user;
}
Enter fullscreen mode Exit fullscreen mode

It solves the problem of what to put in context initial value (we can just put undefined) and allows to do any additional validation. Also, if we decided to refactor in the future and use something else than context for getting user, we can leave codebase untouched and modify just one hook.

Collapse
 
anticoder03 profile image
Ashish prajapati • Edited

Your insights on abstracting useContext with a custom hook like useUser are spot-on. Hereโ€™s a structured breakdown of your points:

1. Potential for Performance Issues ๐Ÿ“‰

  • Unintended Re-renders: Higher-level hooks can cause unnecessary re-renders when context values change. Optimize updates in the context provider to mitigate performance impacts.

2. Error Handling Alternatives โš ๏ธ

  • Error Management: Instead of throwing errors, consider:
    • Returning a fallback value.
    • Logging errors for better debugging.
    • Ensuring the hook is safe for non-essential contexts.

3. Keep an Eye on Complexity ๐Ÿงฉ

  • Balancing Complexity: While abstraction aids in future-proofing and validation, it can complicate simpler contexts. For straightforward data, direct useContext calls may be more efficient and clearer.
Collapse
 
brense profile image
Rense Bakker

Think of it as a combination of componentDidMount, componentDidUpdate, and componentWillUnmount.

No! Stop thinking about deprecated lifecycle methods! It's a different paradigm that doesn't apply to modern React anymore.

Reducers don't have to be complex, they're actually great for managing objects in state in a much cleaner way by leveraging spread syntax.

You should use useMemo and useCallback anytime you have state that will otherwise not be referentially equal. Basically anything that's not a string, number or boolean. Otherwise you're producing excessive or incomplete renders in your React app, which can cause additional bugs or unrealistic cases that you need to make conditions for.

Collapse
 
anticoder03 profile image
Ashish prajapati

Thanks for sharing these insights! Your points really emphasize key aspects of modern React. Itโ€™s great to see such thoughtful discussion around effective useEffect usage, simplifying reducers, and strategic memoization. This perspective keeps React apps lean and efficientโ€”awesome advice! ๐Ÿ™Œ