Search code examples
reactjsreact-router-dom

passing data from the element to the loader in react router dom 6.14


  1. I couldn't find a way to pass props from the context to the loader function (or even from the element that use that loader )
  • i dont want pass the data as params due to in some cases it could be objects.
  1. currently i can create spinner in each element that loaded by the router , but i guess there is a way to apply spinner from the router loader itself , couldnt find a way

This is the Index.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import router from "./Routs/Routs";
import { RouterProvider } from "react-router-dom";
import { SiteProvider } from "./hooks/useContext";

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
  <React.StrictMode>
    <SiteProvider>
      <RouterProvider router={router} />
    </SiteProvider>
  </React.StrictMode>
);

and the router

const router = createBrowserRouter([
  {
    path: "/",
    element: <App />,
    errorElement: <ErrorPage />,
    children: [
      {
        path: "/products/:category",
        element: <ProductsMain />,
        loader: getItems,
      }
    ],
  }
]);

the getItems function:

export async function getItems(request: any) {
  const body = {
    some data//:how do i pass a data to the body from context or Other element as props?,
  };
  try {
    const url = `${urlBE}/products/${request.params.category}`;
    // console.log(request);
    return await serverAPI(url, body, "success");
  } catch (error: any) {
    throw new Error(error);
  }
}

And now in the ProductData i can use the data from the loader ass follow:

  const { data, message, error }: any = useLoaderData();
  useEffect(() => {
    data && setProducts(data);
  }, [data]);

Solution

  • Short of implementing something more globally accessible like Redux where you can just import the store object and access current state or dispatch actions my suggestion here would be to use a Context Consumer to access and pass the context value to the router so it can be passed to loader functions.

    The following example may be close to what you are looking for.

    export async getItems = (contextValue: SiteContextType) => (request: any) => {
      const body = {
        // some data accessible via provided context value
      };
    
      try {
        const url = `${urlBE}/products/${request.params.category}`;
        return await serverAPI(url, body, "success");
      } catch (error: any) {
        throw new Error(error);
      }
    }
    
    const router = (contextValue: SiteContextType) => createBrowserRouter([
      {
        path: "/",
        element: <App />,
        errorElement: <ErrorPage />,
        children: [
          {
            path: "/products/:category",
            element: <ProductsMain />,
            loader: getItems(contextValue),
          }
        ],
      }
    ]);
    
    root.render(
      <React.StrictMode>
        <SiteProvider>
          <SiteContext.Consumer>
            {value => <RouterProvider router={router(value)} />}
          </SiteContext.Consumer>
        </SiteProvider>
      </React.StrictMode>
    );