Skip to content

ch0ripain/react-context-and-reducer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

34 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Quick Introduction to React Context & Reducer πŸš€

🌐 Context 🌐

In React, a Context is a common way to share state globally across your application. To get started, you first need to create the context. This can be done using a function provided by React ➑️ createContext

import { createContext } from "react";
export const CartContext = createContext({
  items: [],
  addItemToCart: () => {},
  updateCartItemQuantity: () => {},
});

As shown above, we need to import createContext to make it available for use. The createContext function can accept any JavaScript expression, such as [], {}, 5, or 'context'

In this example, we define an object with some properties initialized to undefined values. This is a common best practice as it allows us to clearly define the properties of our context while enabling code autocompletion in our editor.

A standard approach to make the context usable is to wrap it in a component using the Context.Provider property.

export default function CartContextProvider({ children }) {
  const contextValue = {
    items: shoppingCartState.items,
    addItemToCart: handleAddItemToCart,
    updateCartItemQuantity: handleUpdateCartItemQuantity,
  };
 return (
    <CartContext.Provider value={contextValue}>{children}</CartContext.Provider>
  );
}

Finally, we need to wrap our main component with the context provider. This ensures that the main component and all its child components will have access to the context.

import CartContextProvider from "./store/shopping-cart-context.jsx";

function App() {
  return (
    <CartContextProvider>
      <Header />
      <Shop>
        {DUMMY_PRODUCTS.map((product) => (
          <li key={product.id}>
            <Product {...product} />
          </li>
        ))}
      </Shop>
    </CartContextProvider>
  );
}

πŸŒ€ Reducer πŸŒ€

const [shoppingCartState, shoppingCartDispatch] = useReducer(
    shoppingCartReducer,
    {
      items: [],
    }
  );
  //...
  <p>{items.length == 0 && No items :(}</p>
  //...

As shown above, the shoppingCartState should be treated as a read-only value.

On the other hand, shoppingCartDispatch is the dispatcher function that we use to pass the updated state and the type of change we want to perform.

The useReducer() hook takes two arguments:

  • A function that contains all the logic for updating the state.
  • The initial state value.

In essence, useReducer is a hook similar to useState, but all state updates are managed in a separate function, providing a more structured way to handle complex state logic.

function shoppingCartReducer(state, action) {
  if (action.type === "ADD_ITEM") {
    const updatedItems = [...state.items];

    const existingCartItemIndex = updatedItems.findIndex(
      (cartItem) => cartItem.id === action.payload.id
    );
    //...
  }
}

shoppingCartReducer function takes two parameters:

  • state ➑️ the most recent state when the reducer function is called.
  • action ➑️ typically an object containing key-value pairs, such as {type: 'ADD_ITEM', payload: id}

🌐 Using context 🌐

To use a context, we need to import two things:

import { useContext } from "react";
import { CartContext } from "../store/shopping-cart-context";

Next, we can destructure the specific context we need from the context object we previously created.

const { items, updateCartItemQuantity } = useContext(CartContext);

Finally, we use the context as intended in our application.

const totalPrice = items.reduce(
    (acc, item) => acc + item.price * item.quantity,
    0
  );
 <ul id="cart-items">
          {items.map((item) => {
          //...
          <button onClick={() => updateCartItemQuantity(item.id, -1)}>
          //...
          }
</ul>

Note

Prop drilling is passing data through multiple layers of components via props. Context avoids this by providing a way to share data directly with any component, no matter how deep it is in the tree.

With all the above, we have implemented a global state using context, avoiding prop drilling and separating the state update logic using a reducer. This approach makes our code cleaner, more maintainable, and easier to understand.


🐸 This project is a practice exercise I learned from the Academind's React Course 🐸