React Typescript Context - issue with mapping object

Viewed 157

I'm getting an error with mapping an object. I've spent hours trying to solve this issue but I have no clue what I'm doing wrong :(

Error:

const categoriesMap: {
    item: Product[];
}
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ item: Product[]; }'.
  No index signature with a parameter of type 'string' was found on type '{ item: Product[]; }'.ts(7053)

shop.component.tsx error comes from {categoriesMap[title].map}

const Shop: React.FC = () => {
  const { categoriesMap } = useContext(CategoriesContext);

  console.log(categoriesMap);

  return (
    <Fragment>
      {Object.keys(categoriesMap).map((title) => {
        console.log(title);
        return (
          <Fragment key={title}>
            <h2>{title}</h2>
            <div className={classes["products-container"]}>
              {categoriesMap[title].map((product: any) => (
              <ProductCard key={product.id} product={product} />
            ))}
            </div>
          </Fragment>
        );
      })}
    </Fragment>
  );
};

export default Shop;

categories.context.tsx Argument of type '{}' is not assignable to parameter of type 'ShopData | (() => ShopData)'.ts(2345) error on the line with useState({})

interface AppContextInterface {
  // [key: string]: [] | Array<[]>
  [categoriesMap: string]: {
    item: Product[];
  };
}

export const CategoriesContext = createContext<AppContextInterface>(
  {} as AppContextInterface
);

export const CategoriesProvider = ({ children }: { children: ReactNode }) => {
  const [categoriesMap, setCategoriesMap] = useState<ShopData>({});

  useEffect(() => {
    const getCategoriesMap = async () => {
      const categoryMap = await getCategoriesAndDocuments();
      setCategoriesMap(categoryMap);
    };
    getCategoriesMap();
  }, []);

  const value = { categoriesMap };

  return (
    <CategoriesContext.Provider value={value}>
      {children}
    </CategoriesContext.Provider>
  );
};

shop-data.ts

export interface Product {
  id: number;
  name: string;
  imageUrl: string;
  price: number;
}

export interface ShopData {
  title: string;
  items: Product[];
}

I will appreciate any help!

2 Answers

I think the issue is that useState<ShopData>({}) expect the initial state of use state of being ShopData.

Shop data is:

export interface ShopData {
  title: string;
  items: Product[];
}

Therefore the object {} in useState<ShopData>({}) is not of type ShopData.

I've managed to fix the error by myself. I thought I will post it anyway. It could help someone in the future:

categories.context.tsx

export const CategoriesContext = createContext<ShopData>({} as ShopData);

export const CategoriesProvider = ({ children }: { children: ReactNode }) => {
  const [categoriesMap, setCategoriesMap] = useState<ProductObject>({});

  useEffect(() => {
    const getCategoriesMap = async () => {
      const categoryMap = await getCategoriesAndDocuments();
      setCategoriesMap(categoryMap);
    };
    getCategoriesMap();
  }, []);

  const value = { categoriesMap };

  return (
    <CategoriesContext.Provider value={value}>
      {children}
    </CategoriesContext.Provider>
  );
};

shop-data.ts

export interface Product {
  id: number;
  name: string;
  imageUrl: string;
  price: number;
}

export interface ProductObject {
  [title: string]: Product[];
}

export interface ShopData {
  categoriesMap: ProductObject;
}

I've broken code down into a very small pieces and used a lot of typeof operator what helped a lot. Thank you for looking at the issue anyway!

Related