Search code examples
javascriptreactjsreact-router-dom

useLoaderdata gives error 'useLoaderData must be used within a data router' in react


I am trying to load API from the routes using loader, params, and useLoaderData hook.

I used loader params in my existing routing structure and useLoaderData hook in my component but it didn't work. I checked other questions, but they primarily work on single component routing structure, but for me routing for all components are already done. How can I modify my router accordingly?

<BrowserRouter>
  <Header />
  <Routes>
    <Route path='/' element={<Home />} />
    <Route path='/about' element={<About />} />
    <Route path='/services' element={<Services />} >
      <Route element={<WebServices />} index />
      <Route path='web' element={<WebServices />} />
      <Route path='app' element={<AppServices />} />
      <Route path='cloud' element={<CloudServices />} />
    </Route>
    <Route path='/blog' element={<Blog />} loader={fetchPosts} />
    <Route path='*' element={<Page404 />} />
  </Routes>
</BrowserRouter>
function Blog(props) {
  const list = useLoaderData();

  return (
    <div className='blog-container'>
      <div className='container'>
        <h1>Your Blog comes here</h1>
      </div>
      <div className='blog-post-container'>
        {list && list.length > 0
          ? list.map((item, index) => {
            return (
              <div className='blog-card'></div>
            )
          }) : <p>No post found</p>
        }
      </div>
    </div>
  )
}

Solution

  • The data loaders can only be used in data routers. Convert your code from using the BrowserRouter to using createBrowserRouter.

    import {
      createBrowserRouter,
      createRoutesFromElements
      Route,
      Outlet,
    } from "react-router-dom";
    
    // Create a layout route component so the header is usable 
    // and can access the routing context for links, etc.
    const AppLayout = () => (
      <>
        <Header />
        <Outlet />
      </>
    );
    
    export const router = createBrowserRouter(
      createRoutesFromElements(
        <Route element={<AppLayout />}>
          <Route path='/' element={<Home />} />
          <Route path='about' element={<About />} />
          <Route path='services' element={<Services />} >
            <Route element={<WebServices />} index />
            <Route path='web' element={<WebServices />} />
            <Route path='app' element={<AppServices />} />
            <Route path='cloud' element={<CloudServices />} />
          </Route>
          <Route path='blog' element={<Blog />} loader={fetchPosts} />
          <Route path='*' element={<Page404 />} />
        </Route>
      )
    );
    

    Render the router into the RouterProvider component

    import { RouterProvider } from "react-router-dom";
    
    ...
    
    <RouterProvider router={router} />
    

    Just FYI a route configuration approach may look like the following:

    export const router = createBrowserRouter([
      {
        element: <AppLayout />,
        children: [
          { path: "about", element: <About /> },
          {
            path: "services",
            element: <Services />,
            children: [
              { index: true, element: <WebServices /> },
              { path: "web", element: <WebServices /> },
              { path: "app", element: <AppServices /> },
              { path: "cloud", element: <CloudServices /> },
            ],
          },
          {
            path: "blog",
            loader: fetchPosts,
            element: <Blog />,
          },
          { path: "*", element: <Page404 /> },
        ],
      },
    ]);
    

    createRoutesFromElements is a convenience utility function.