React JS Beginner to Advanced series (5)

Aryan kadamAryan kadam
5 min read
  • Before diving into this blog, make sure you've checked out the part-1 of this blog in this series on my profile: ReactJS Beginner to Advanced Series

  • That blog will provide you with essential knowledge and context for understanding the concepts discussed here. Once you're caught up, let's explore more about Hooks in React!

  • In last blog of this series we cover (useState, useEffect, useContext) hooks, lets dive into more.

useReducer:

  • useReducer is as an alternative to useState for managing state in functional components, especially when the state logic is complex and involves multiple sub-values or actions.
  • If you find yourself keeping track of multiple pieces of state that rely on complex logic, useReducer may be useful.

syntax:

  • The useReducer Hook accepts two arguments.
    useReducer(<reducer>, <initialState>)
    

Use useReducer when:

  • State Logic is Complex: If your state logic involves multiple sub-values or actions that depend on previous state, useReducer can help manage this complexity more effectively than useState.
  • Interrelated State Updates: When updating one state value depends on the current value of another state, or when state updates need to be synchronous and predictable, useReducer provides a more controlled way to handle these updates.
  • Sharing State Logic: If you find yourself duplicating state logic across multiple components, useReducer can encapsulate that logic into a single place and make it reusable across components.

Simple Use Case:

  • Let's take a example of shopping cart feature for an e-commerce app. You have items in the cart, and you need to add, remove, or update the quantity of items.
import React, { useReducer } from 'react';

const initialState = {
  items: [],
};

function reducer(state, action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return {
        ...state,
        items: [...state.items, action.payload],
      };
    case 'REMOVE_ITEM':
      return {
        ...state,
        items: state.items.filter(item => item.id !== action.payload.id),
      };
    case 'UPDATE_QUANTITY':
      return {
        ...state,
        items: state.items.map(item =>
          item.id === action.payload.id
            ? { ...item, quantity: action.payload.quantity }
            : item
        ),
      };
    default:
      return state;
  }
}

function ShoppingCart() {
  const [state, dispatch] = useReducer(reducer, initialState);

  // Functions to dispatch actions
  const addItem = (item) => {
    dispatch({ type: 'ADD_ITEM', payload: item });
  };

  const removeItem = (item) => {
    dispatch({ type: 'REMOVE_ITEM', payload: item });
  };

  const updateQuantity = (itemId, newQuantity) => {
    dispatch({ type: 'UPDATE_QUANTITY', payload: { id: itemId, quantity: newQuantity } });
  };

  return (
    <div>
      {/* Render shopping cart items */}
    </div>
  );
}

useRef:

  • useRef is hook that allows you to create a reference to a DOM node or a javascript object. it can be used to access a DOM node directly, or to store a value that should not cause a re-render when it changes.

Use useRef when:

  • Access DOM Elements: If you need to interact with a DOM element directly, such as focusing an input field, scrolling to a particular section, or measuring its dimensions, useRef can help.
  • Store Mutable Values: If you have a value that you want to persist across renders without causing a re-render, such as a timer ID or a cached value, useRef is a good choice.
  • Imperative DOM Manipulation: When you need to perform imperative DOM manipulation, such as triggering animations or integrating with third-party libraries that expect a DOM reference, useRef can be handy.

Simple Use Case:

  • Let's take a example of a form with an input field, and you want to focus on that input field when the component mounts. You can use useRef to achieve this:
import React, { useRef, useEffect } from 'react';

function MyForm() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return (
    <form>
      <input ref={inputRef} type="text" />
      <button type="submit">Submit</button>
    </form>
  );
}

useMemo:

useMemo is a hook that allows you to memoize a value. it is used to optimize the performance of a component by only re-computing a value if its dependencies have changed. this can be useful for avoding expensive calculations or rendering operations.

Use useMemo when:

  • Expensive Calculations: If you have a function that performs complex or computationally expensive calculations, and the result doesn't change frequently, useMemo can help optimize performance by caching the result.
  • Avoiding Unnecessary Recalculation: When you have a function that depends on certain inputs and you want to avoid recalculating its result unless those inputs change, useMemo is useful.
  • Optimizing Re-renders: If you're experiencing performance issues due to unnecessary re-renders caused by expensive computations, useMemo can be a solution to optimize rendering.

Simple Use Case:

  • Let's take an example where you have a component that renders a list of items, and you want to compute the total price of all items. You can use useMemo to memoize the computation of the total price:
import React, { useMemo } from 'react';

function ShoppingCart({ items }) {
  const totalPrice = useMemo(() => {
    return items.reduce((total, item) => total + item.price, 0);
  }, [items]);

  return (
    <div>
      <h2>Total Price: ${totalPrice}</h2>
      {/* Render the list of items */}
    </div>
  );
}

useCallback:

-useCallback is a hook that memoizes a callback function. It returns a memoized version of the function that only changes if one of the dependencies has changed. This can help optimize performance by avoiding unnecessary re-creations of functions.

Use useCallback when:

  • Preventing Unnecessary Re-renders: If you have a callback function that is passed down to child components as a prop, and that function is created inside a parent component, you can use useCallback to memoize the function and prevent unnecessary re-renders of child components.
  • Optimizing Performance: When you're dealing with callback functions that are computationally expensive to create, such as functions that involve heavy calculations or fetching data from an API, useCallback can help optimize performance by memoizing the function.

Simple Use Case:

  • Let's take an example where you have a parent component that renders a list of items, and each item has a button to delete it. You want to pass a callback function to each item component to handle the delete action. You can use useCallback to memoize the callback function:
import React, { useCallback } from 'react';

function ItemList({ items, onDeleteItem }) {
  const handleDelete = useCallback((itemId) => {
    onDeleteItem(itemId);
  }, [onDeleteItem]);

  return (
    <div>
      {items.map(item => (
        <div key={item.id}>
          <span>{item.name}</span>
          <button onClick={() => handleDelete(item.id)}>Delete</button>
        </div>
      ))}
    </div>
  );
}

More to Go:

  • Thank you for reading this blog and keep following till now, in the blog of this series we contains topics like creating routing and navigation between them, client side routing. Stay tuned for more insightful content!
0
Subscribe to my newsletter

Read articles from Aryan kadam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Aryan kadam
Aryan kadam

I am a student, coder, tech enthusiast, open source contributor, whatever you say.