qweezz
qweezz

Reputation: 804

How to get state from different slices in another slice thunk and do something with it?

I have three slices ProductSlice, CategorySlice and BrandSlice. Each slice has createAsyncThunk which makes fetch request for products, categories and brands.

CategorySlice (BrandSlice the same):

export const fetchCategories = createAsyncThunk('category/fetchCategories', async () => {
  const response = await fetch(`${BASE_URL}/categories.json`);
  const data = await response.json();
  const categories: Category[] = handleObj(data);
  return categories;
});

    builder.addCase(fetchCategories.fulfilled, (state, { payload }) => {
      state.categories = payload;
      state.isLoading = false;
    });

App component dispatches three fetch requests.

  useEffect(() => {
     dispatch(fetchCategories());
     dispatch(fetchProducts());
     dispatch(fetchBrands()); 
  }, [dispatch]);

I need to get data from brands and categories. Then get data for products and add some data to each product and only after that set it to ProducSlice state. The data should come from categories and brands. I'm trying to it in ProductSlice createAsyncThunk this way:

export const fetchProducts = createAsyncThunk<Product[], void, { state: RootState }>(
  'product/fetchProducts',
  async (_, { getState }) => {
    const response = await fetch(`${BASE_URL}/products.json`);
    const data = await response.json();
    const products: Product[] = handleObj(data);

    const { brands } = await getState().brand;
    const { categories } = await getState().category;

    const productsWithUpdatedBrandsAndCategories = products.map((product) => {
      const category = categories.find((category) => category.id === product.category.id);
      const brand = brands.find((brand) => brand.id === product.brand.id);

      if (!category && !brand) return product;

      return {
        ...product,
        category: {
          ...category,
          name: category && category.name,
        },
        brand: {
          ...brand,
          name: brand && brand.name,
        },
      };
    }) as Product[];

    return productsWithUpdatedBrandsAndCategories;
  }
);

The problem is that from time to time the brands or categories in ProductSlice in thunk are empty arrays (I mean the thunk fires when the data has not came from other slices..so productsWithUpdatedBrandsAndCategories do nothing then). How to deal with that?

Upvotes: 3

Views: 1072

Answers (1)

Drew Reese
Drew Reese

Reputation: 202836

It seems there is a dependency on fetchCategories and fetchBrands and subsequent state updates prior to being able to dispatch fetchProducts to have the brand and categories state available.

I suggest a little refactor to dispatch the first two actions and wait for them to resolve then dispatching the third.

Example:

useEffect(() => {
  const fetchData = async () => {
    try {
      // fetch brands and categories and wait for actions to resolve
      await Promise.all([
        dispatch(fetchBrands()),
        dispatch(fetchCategories()),
      ]);

      // now fetch products
      dispatch(fetchProducts());
    } catch(error) {
      // handle or ignore errors?
    }
  };
  
  fetchData();
}, [dispatch]);

...

export const fetchProducts = createAsyncThunk<Product[], void, { state: RootState }>(
  'product/fetchProducts',
  async (_, { getState }) => {
    const response = await fetch(`${BASE_URL}/products.json`);
    const data = await response.json();
    const products: Product[] = handleObj(data);

    const {
      brand: { brands },
      category: { categories },
    } = getState();

    const productsWithUpdatedBrandsAndCategories = products.map((product) => {
      const category = categories.find((category) => category.id === product.category.id);
      const brand = brands.find((brand) => brand.id === product.brand.id);

      if (!category && !brand) return product;

      return {
        ...product,
        category: {
          ...category,
          name: category && category.name,
        },
        brand: {
          ...brand,
          name: brand && brand.name,
        },
      };
    }) as Product[];

    return productsWithUpdatedBrandsAndCategories;
  }
);

Upvotes: 2

Related Questions