Search code examples
reactjstypescriptreact-context

How to create Reduces for a custom Type


I was working on a shopping website this morning, using React Typescript and Context API, and want to use React Reducers to manipulate my Shopping Cart State with the custom Types I created for the Product type, including an Items Array and some Functions ... The problem is I don't know how to pass parameters with the Action in the CartReducer function, and would like to know how you would do it

My CartContext

import * as React from 'react';
import { useQuery } from "react-query";





type ICartContext = [IProductItem[] | undefined, React.Dispatch<React.SetStateAction<IProductItem[] | undefined>>];

export const CartContext = React.createContext<ICartContext>([[], () => null]);



type Action = "Add" | "Update" | "Remove"

const CartReducer = (state: IProductItem[], action: Action) => {

    return state
}


const CartProvider: React.FC<{}> = ({children}: { children?: React.ReactNode }) => {

    const [cart, setCart] = React.useReducer(CartReducer, [], undefined)


    return (
        <CartContext.Provider value={[cart, setCart]}>
            {children}
        </CartContext.Provider>
    );

};

export default CartProvider;

My Types

interface IProductItem{
    id: number
    title: string
    description: string
    category: string
    image: string
    price: number
    quantity: number
}

type ProductType = {
    items: IProductItem[];
    saveItem: (item: IProductItem) => void
    updateItem: (id: number) => void
    removeItem: (id: number) => void
};

Solution

  • Use a discriminated union as your action's type:

    // each specific action includes a `type` property
    
    interface AddAction {
      type: "Add";
      addedProduct: IProductItem;
    }
    interface UpdateAction {
      type: "Update";
      updatedProduct: IProductItem;
    }
    interface RemoveAction {
      type: "Remove";
      removedProduct: IProductItem;
    }
    
    // Action is a union of all the possible actions
    type Action = AddAction | UpdateAction | RemoveAction;
    
    const CartReducer = (state: IProductItem[], action: Action) => {
      // Since `type` is the only property all actions have in common,
      // it's the only property that can be accessed until narrowing
      // it with a condition or a switch statement
    
      switch (action.type) {
        case "Add":
          // can access action.addedProduct here
          return state;
    
        case "Update":
          // can access action.updatedProduct here
          return state;
    
        case "Remove":
          // can access action.removedProduct here
          return state;
      }
    };
    
    // elsewhere:
    dispatch({ type: "Add", addedProduct: product })