3

I'm using react-persist in my application. When I click to add a product to my basket and then want to navigate to my basket page, how do I pass the basket data to the fetch function in the loader that is in the createBrowserRouter:

This is my function that fetches the basket data:

import { HostName } from './ApiHost';

async function fetchBasketData() {
  return await fetch(`${HostName}api/Product/GetBasketItems`, {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    },
    body: null, // <-- this is where I want to send the basket data
  })
    .then((response) => response.json())
    .then((response) => {
      return response;
    })
    .catch(error => console.warn(error));
}

export {
  fetchProductSelectorsData,
  fetchProductData,
  fetchProductsData,
  fetchBasketData
}

This is my ProductItem that stores the basket items:

const basketClicked = (productGuid, type) => {
  if (type == "add") {
    let basketItem = {
      productGuid: productGuid,
      quantity: 1,
    };
    dispatch(addBasketItem(basketItem));
    setIsBasketItem(true);
  } else {
    var basketItemsArray = basketItems;
    basketItemsArray = basketItemsArray.filter(
      item => item.productGuid !== productGuid
    );
    if (basketItemsArray.length == 0) {
      dispatch(updateBasketItem([]));
    } else {
      dispatch(updateBasketItem(basketItemsArray));
    }
    setIsBasketItem(false);
  }
}

This is my createBrowserRouter:

const router = createBrowserRouter([
  {
    path: '/',
    element: <LayoutComponent />,
    children: [
      {
        index: true,
        element: <Home />,
        loader: fetchProductsData,
      },
      {
        path: '/search',
        element: <Search />,
      },
      {
        path: '/basket',
        element: <Basket />,
        loader: fetchBasketData, // <-- need to pass the basket data inside my fetchBasketData
      },
      { path: '/product/add',
        element: (
          <PrivateRoute>
            <Product />
          </PrivateRoute>
        ),
        loader: fetchProductSelectorsData,
      },
      { path: '/product/listing/:productGuid',
        element: <ProductListing />,
        loader: async ({ params, request }) => {
          console.log("params: " + params.productGuid);
          return fetchProductData(params.productGuid);
        },
      },
      {
        path: '/login',
        element: <Login />,
      },
      {
        path: '/signup',
        element: <Signup />,
      },
      {
        path: '/stripecomplete',
        element: <StripeComplete />,
      },
      {
        path: '/stripeerror',
        element: <StripeError />,
      },
      {
        path: '/selling',
        element: <Selling />,
      },
      {
        path: '/product/stored',
        element: (
          <PrivateProductStoredRoute>
            <ProductStored />
          </PrivateProductStoredRoute>
        ),
      },
      {
        path: '/imagepaymentsuccess',
        element: <ImagePaymentSuccess />,
      },
      // {
      //   path: '/product/imagecheckout',
      //   element: (
      //     <PrivateRoute>
      //       <Suspense fallback={<div>Loading...</div>}>
      //           <ImageCheckout />
      //       </Suspense>
      //     </PrivateRoute>
      //   ),
      //   loader: fetchImageCheckoutData
      // },
      {
        path: '*',
        element: <ErrorPage />,
      },
    ],
  },
]);

Using the useSelector to get the basket items array:

import { useSelector, useDispatch } from 'react-redux';

const basketItems = useSelector((state) => state.globalState.basketItems);

1 Answer 1

1

You can create a curried loader function that consumes any arguments you want to pass to the loader function and returns a loader function.

Example:

Here we're passing the entire Redux store to the loader and then selecting the basketItmes from the current state value when the returned loader function is invoked.

const selectBasketItems = (state) => state.globalState.basketItems;

const fetchBasketData = (store) => async () => {
  // select the current state value
  const basketItems = selectBasketItems(store.getState());

  try {
    const response = await await fetch(`${HostName}api/Product/GetBasketItems`, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(basketItems), // <-- pass basketItems as payload
    });
    return await response.json();
  } catch(error) {
    console.warn(error);
  }
};

Create a memoized router with a store instance passed to the route loader.

import { createBrowserRouter } from "react-router";
import { RouterProvider } from "react-router/dom";
import { useStore } from 'react-redux';

...

const App = () => {
  const store = useStore();

  const router = React.useMemo(() => createBrowserRouter([
    {
      path: '/',
      element: <LayoutComponent />,
      children: [
        ...
        {
          path: '/basket',
          element: <Basket />,
          loader: fetchBasketData(store), // <-- pass store to loader
        },
        ...
      ],
    },
  ]), [store]);

  ...

  return (
    ...
    <RouterProvider router={router} />
    ...
  );
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.