Search code examples
javascriptreactjsreact-routerreact-query

React-Query / React Router V5 insert state variable from api response into route but only when response has finished


In my app, I have an api call which gets some data (Array of objects), I then use this in a context and share this across my app. What I want to do is use some of that data i.e object value and prefix it to my routes. The API handling is done via react-query.

I have tried just accessing the variable and inserting it as follows:

const Routing: React.FunctionComponent = () => {
  const { valueFromContext } = React.useContext(DataContext);

  const basePrefix = valueFromContext?.name;

  return (
    <>
        <BrowserRouter >
          <Header />
          <div className="App">
            <Switch>
              <Route exact path={["/", "/dashboard"]}>
                <Redirect to={`/${basePrefix}/home`} />
              </Route>
              <Route exact path={[`/${basePrefix}/home`, "/"]} component={Home} />
              <Route exact path={`/account/:id`} render={(props: RouteComponentProps<any>) => <Account {...props} />} />
              <Route component={NotFound} />
            </Switch>
          </div>
        </BrowserRouter>
    </>
  )
}

So problem is this is inconsistent, as sometimes the url comes back as '/undefined/home' and shows the 404 not found component, so I am thinking the route is being inserted before the response has finished?

Any idea's?


Solution

  • Yes, it appears that on the initial render cycle that basePrefix, or valueFromContext?.name, yields an undefined value. You can apply some conditional rendering logic to return null or a loading indicator while the basePrefix value is fetched/computed.

    Example:

    const Routing: React.FunctionComponent = () => {
      const { valueFromContext } = React.useContext(DataContext);
    
      const basePrefix = valueFromContext?.name;
    
      return basePrefix
        ? (
          <BrowserRouter >
            <Header />
            <div className="App">
              <Switch>
                <Route exact path={["/", "/dashboard"]}>
                  <Redirect to={`/${basePrefix}/home`} />
                </Route>
                <Route exact path={[`/${basePrefix}/home`, "/"]} component={Home} />
                <Route exact path={`/account/:id`} render={(props: RouteComponentProps<any>) => <Account {...props} />} />
                <Route component={NotFound} />
              </Switch>
            </div>
          </BrowserRouter>
        )
        : null; // <-- or loading indicator/spinner/etc
    }
    

    In your closely related question here notice that I provided a valid defined baseprefix value from the context for this reason, to not accidentally inject undefined into the URL path.