MadGrip
MadGrip

Reputation: 471

How to setup useReducer with TypeScript properly?

I'm learning TypeScript and I have a difficulty to setup my reducer properly.

BasketContext.tsx

import React, { useContext, useReducer } from "react";

import BasketReducer from "../reducers/BasketReducer";

const BasketContext = React.createContext<any>(undefined);

export interface IBasketState {
    products: [
        {
            name: string;
            price: number;
            quantity: number;
        }
    ];
    totalPrice: number;
}

const initialState: IBasketState = {
    products: [
        {
            name: "",
            price: 0,
            quantity: 0,
        },
    ],
    totalPrice: 0,
};

export const BasketContextProvider = ({ children }: { children: ReactNode }) => {
    const [basketState, dispatch] = useReducer<IBasketState>(BasketReducer, initialState);

 

    return <BasketContext.Provider value={{ basketState }}>{children}</BasketContext.Provider>;
};

export const useBasketContext = () => {
    return useContext(BasketContext);
};

BasketReducer.ts

import React from "react";

import { IBasketState } from "../contexts/BasketContext";

interface IBasketAction {
    type: "Add item" | "Remove item";
    payload?: string;
}

const BasketReducer = (basketState: IBasketState, action: IBasketAction) => {
    if (action.type === "Add item") {
        console.log("Add iteeeeeeem");
        return { ...basketState };
    }
};

export default BasketReducer;

When I hover my mouse over const [basketState, dispatch] = useReducer<IBasketState>(BasketReducer, initialState); I've got an error:

Type 'IBasketState' does not satisfy the constraint 'Reducer<any, any>'. Type 'IBasketState' provides no match for the signature '(prevState: any, action: any): any'.ts(2344)

Also is it possible to initialize my initialState with empty array products? Currently I use empty string and 0.

Upvotes: 0

Views: 1052

Answers (1)

spender
spender

Reputation: 120450

Your reducer function must return a state (either a new state or the unmodified, passed-in state) in all cases. In your code, you are implicitly returning undefined if there's no match for the incoming action.

// add a return-type annotation
const BasketReducer = 
  (basketState: IBasketState, action: IBasketAction) : IBasketState => {
    if (action.type === "Add item") {
        console.log("Add iteeeeeeem");
        return { ...basketState };
    }
    return basketState; // no match, return same state
};

You are also using incorrect generic types for your useReducer<IBasketState>(... call. The generic types for this call are a little complex, but you should be able to remove the generic type annotations and just let the compiler correctly infer the right types for your reducer i.e.:

useReducer(BasketReducer, initialState)

Upvotes: 3

Related Questions