1

I like to prefetch some data ahead of time so I prepared the routes and the loader like below and I'm getting infinite loop. Someone to suggest what is wrong and how I fix this.

homeLoader function:

  homeLoader: async (queryClient) => {
    queryClient.ensureQueryData(['someKey'], doSomething);
    return redirect(`workspaces/1`); // Infinite loop happens
  }
};

router for the RouterProvider:

createBrowserRouter(
  createRoutesFromElements(
    <Route
      path='/'
      element={<Home />}
      loader={() => homeLoader(queryClient)} // React Query injected
    >
      <Route path='workspaces' element={<Workspaces />}>
        <Route path=':workspaceId' element={<Workspace />} />
      </Route>
    </Route>
  )
)

<RouterProvider router={router} />

React and Router versions:

"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.14.2"
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • You shouldn't be calling a redirect in the loader. The loader is meant to just fetch data for the route you are redirecting to. So if you call a redirect in the loader itself, it will cause the infinite loop. Why are you trying to redirect as part of the loader? – M0nst3R Aug 21 '23 at 15:34
  • @M0nst3R because that's how our routes are structured (first '/' then goes to some child route), and we have immediate redirect (after some data is available from the server). I don't like to use useEffect and index route, but to make redirect through the loader. – Игор Ташевски Aug 21 '23 at 15:40
  • What's wrong with `useEffect()`? At least it won't cause an infinite loop. Take a look at this [post](https://stackoverflow.com/questions/58444065/redirecting-using-useeffect-hook) and see if it helps you. – M0nst3R Aug 21 '23 at 15:43
  • @M0nst3R Just I don't like it, I like more naive approach. I already have the implementation with useEffect, but I was expecting the loader once per route. – Игор Ташевски Aug 21 '23 at 16:03
  • _I was expecting the loader once per route._ The problem is loader causes another route, which causes the loader, which causes the route... – M0nst3R Aug 21 '23 at 16:04
  • 1
    @M0nst3R manage to fix as: } loader={() => homeLoader(queryClient)} > redirect('someRoute')} /> – Игор Ташевски Aug 21 '23 at 16:08

1 Answers1

1

It looks like you are trying to curry the queryClient to the loader function. The homeLoader could be rewritten to accept a queryClient argument and then return a function that serves as the loader function for the route.

Additionally, unconditionally returning a redirect from the loader is the likely cause of the render looping. If you want "/" to render a "default route" then use the Navigate component and issue the redirect from an index route.

Example:

homeLoader: (queryClient) => () => {
  queryClient.ensureQueryData(['someKey'], doSomething);
}
import {
  createBrowserRouter,
  createRoutesFromElements,
  Route,
  Navigate,
} from 'react-router-dom';

...

createBrowserRouter(
  createRoutesFromElements(
    <Route
      path='/'
      element={<Home />}
      loader={homeLoader(queryClient)} // React Query injected
    >
      <Route index element={<Navigate to="/workspaces/1" replace />} />
      <Route path='workspaces' element={<Workspaces />}>
        <Route path=':workspaceId' element={<Workspace />} />
      </Route>
    </Route>
  )
)
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks. This is looks better, with the Navigate component. What the currying will help me (I can't make it work the redirect, if I change the function with currying), I saw the same way of writing the loader on the RQ blog: rq client meets react router. – Игор Ташевски Aug 21 '23 at 19:13
  • @ИгорТашевски Are you asking what/how writing a curried function helps? It basically saves passing an anonymous callback function. For example, instead of something like `myFunction = (customArg, event) => { ... }` and `callback: event => myFunction(customArg, event)` where the anonymous function exists only to proxy the event arg, you can curry the event arg with `myFunction = customArg => event => { ... }` and then `callback: myFunction(customArg)`. Sorry if using currying here has caused any confusion, the main issue was the loader returning a redirect. – Drew Reese Aug 21 '23 at 19:47
  • More I liked to ask, how curriyng helps in react router context, but it didn't help me in my case I like to take rid of some useEffects per page, and just move that useEffect code into this loader function (even sometimes I don't fetch anything), so I moved the ReactQueryProvider in index.tsx file, and instead inject it, I can use the same queryClient instance with: queryClient = useQueryClient(), and call the loaders without any params. – Игор Ташевски Aug 21 '23 at 20:03
  • @ИгорТашевски No benefit, or detriment, of using the curried loader function in relation to React-Router. They are independent. Using a curried function is more of a preference I suppose, but functionally it is the same as using the anonymous function. – Drew Reese Aug 21 '23 at 20:12