Search code examples
javascriptreactjsreact-routerreact-router-dom

Can useLoaderData be used in the sibling components of the component in which loader() is used in ReactRouter v6.9.0. If yes then how?


I have fetched data using loader in my MoviePage component and I am using this data by using useLoaderData. Now I want to use this data in sibling components like MoviePage/Genre1, MoviePage/Genre2 etc. For this I am trying to use useLoaderData in the sibling components, but the result is being read as 'undefined'. Does this mean that useLoaderData can be used only in the route component in which loader has been used. If yes, then how can I use the result returned in the other Genre components?

This is how my routes are defined.

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />, // Wrapper Component
    children: [
      {
        index: true,
        element: <HomePage />,
      },
      {
        path: "movies",
        element: <Root2 />,
        children: [
          {
            index: true,
            element: <MoviePage />,
            loader: async () => {
              const options = {
                method: "GET",
                url: "https://ott-details.p.rapidapi.com/advancedsearch",
                params: {
                  start_year: "1970",
                  end_year: "2020",
                  min_imdb: "6",
                  max_imdb: "10",
                  genre: "action",
                  language: "english",
                  type: "movie",
                  sort: "latest",
                  page: "1",
                },
                headers: {
                  "X-RapidAPI-Key": "{api-key}",
                  "X-RapidAPI-Host": "ott-details.p.rapidapi.com",
                },
              };

              const response = await axios.request(options);
              if (!response.ok) {
                return response.data;
              } else {
                console.log(response.status);
              }
            },
          },
          { path: "Genre1", element: <Genre1 /> },
          { path: "Genre2", element: <Genre2 /> },
          { path: "Genre3", element: <Genre3 /> },
          { path: ":genre/:movieId", element: <MovieDetail /> },
        ],
      },
    ],
  },
]);               

Solution

  • You might be looking to use the useRouteLoaderData hook instead of the useLoaderData hook.

    useRouteLoaderData

    This hook makes the data at any currently rendered route available anywhere in the tree. This is useful for components deep in the tree needing data from routes much farther up, as well as parent routes needing the data of child routes deeper in the tree.

    Important: This feature only works if using a data router, see Picking a Router

    You may only need to provide an id prop for the route. You may need to promote the loader function to the parent layout route of the MoviePage and genre routes/components. This is so the data remains available when the nested routes change.

    const movieLoader  = async () => {
      const options = {
        method: "GET",
        url: "https://ott-details.p.rapidapi.com/advancedsearch",
        params: {
          start_year: "1970",
          end_year: "2020",
          min_imdb: "6",
          max_imdb: "10",
          genre: "action",
          language: "english",
          type: "movie",
          sort: "latest",
          page: "1",
        },
        headers: {
          "X-RapidAPI-Key": "{api-key}",
          "X-RapidAPI-Host": "ott-details.p.rapidapi.com",
        },
      };
    
      const response = await axios.request(options);
      if (!response.ok) {
        return response.data;
      } else {
        console.log(response.status);
      }
    },
    
    const router = createBrowserRouter([
      {
        path: "/",
        element: <Root />,
        children: [
          { index: true, element: <HomePage /> },
          {
            id: "movies", // <-- add route id
            path: "movies",
            element: <Root2 />,
            loader: movieLoader, // <-- promote loader
            children: [
              { index: true, element: <MoviePage /> },
              { path: "Genre1", element: <Genre1 /> },
              { path: "Genre2", element: <Genre2 /> },
              { path: "Genre3", element: <Genre3 /> },
              { path: ":genre/:movieId", element: <MovieDetail /> },
            ],
          },
        ],
      },
    ]);
    

    In any of the genre components, use the useRouteLoaderData hook and provide the appropriate route id.

    const movieData = useRouteLoaderData("movies");