DEV Community

Elham Najeebullah
Elham Najeebullah

Posted on

React & TypeScript: How to use Context API and useReducer with Firestore Database?

Here is an example of how you could create a React Context API and use the useReducer hook with TypeScript to manage state for a list of Amazon products stored in a Firestore database and more explanation for the code.

import React, { createContext, useReducer, Reducer, useEffect } from 'react';
import { db } from './firebase';

// Create a context for the products
export const ProductsContext = createContext<{
  products: Product[];
  dispatch: React.Dispatch<ProductAction>;
}>({
  products: [],
  dispatch: () => {},
});

// Define the types for the state and actions
type Product = {
  id: string;
  name: string;
  price: number;
};

type ProductAction =
  | { type: 'ADD'; product: Product }
  | { type: 'DELETE'; id: string }
  | { type: 'UPDATE'; id: string; product: Product }
  | { type: 'SET'; products: Product[] };

// Define the reducer function
const productsReducer: Reducer<Product[], ProductAction> = (state, action) => {
  switch (action.type) {
    case 'ADD':
      return [...state, action.product];
    case 'DELETE':
      return state.filter((product) => product.id !== action.id);
    case 'UPDATE':
      return state.map((product) =>
        product.id === action.id ? action.product : product
      );
    case 'SET':
      return action.products;
    default:
      return state;
  }
};

//In React version 18
type ProductsProviderProp = {
   children: ReactNode;
}

// Create a provider component for the products context
const ProductsProvider: React.FC<ProductsProviderProp> = ({ children }) => {
  const [products, dispatch] = useReducer(productsReducer, []);

  // Load the products from the Firestore database when the component mounts
  useEffect(() => {
    const unsubscribe = db.collection('products').onSnapshot((snapshot) => {
      const products: Product[] = [];
      snapshot.forEach((doc) => {
        products.push({ id: doc.id, ...doc.data() });
      });
      dispatch({ type: 'SET', products });
    });

    // Unsubscribe from the snapshot listener when the component unmounts
    return () => unsubscribe();
  }, []);

  // Return the products context provider with the current state and dispatch function
  return (
    <ProductsContext.Provider value={{ products, dispatch }}>
      {children}
      ...
Enter fullscreen mode Exit fullscreen mode

This is just to show you how to use React and Typescript with Context API and useReducer hook,not a complete app.

Got any questions, please leave a comment.

Top comments (4)

Collapse
 
elhamnajeebullah profile image
Elham Najeebullah

I have made a small update. In React version 18 you will get this error "Property ‘children’ does not exist on type ". This is because the type signature has changed in the recent version of React.

The children prop was removed from React.FunctionComponent (React.FC) so you have to declare it explicitly in your component properties.

Like this:

type Props = {
  children?: React.ReactNode
};
const Component: React.FC<Props> = ({children}) => {...}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
brense profile image
Rense Bakker

You can also use the built-in props with children like this:

const Component = ({children}:React.PropsWithChildren<unknown>) => {
  // Do your component stuff...
}
Enter fullscreen mode Exit fullscreen mode

You can replace unknown with whatever other prop types you want, like so:

const Component = ({children, name}:React.PropsWithChildren<{name: string}>) => {
  // Do your component stuff...
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
kswat profile image
kswat

Does this mean, ithe above code is not updated?

Collapse
 
kswat profile image
kswat • Edited

sorry Elham, the article is too short and did not help me :( Eg. const unsubscribe - what does unsubscribe mean...is it like load ?. There is more "incomplete" code than any explanation. hope you will create clearer articles