'React Router: Conditional redirect if not query parameter present

I am trying to do a conditional redirect, if there is no 'state' query parameter in the url, I want to redirect to an error page.

I am trying to do it in my App.tsx. Since at this point I can not use the hook: useLocation or useHistory, I am getting the query parameter using:

export default function App(): JSX.Element {

  const urlQuery = new URLSearchParams(window.location.search);
  const stateParam = urlQuery.get('state');
  ...
} 

then:

<BrowserRouter>
  {!stateParam && <Redirect to="/error?state=1" />}
  <Route path="/" exact component={HomeScreen} />
  <Route path="/t1" exact component={T1Screen} />
  <Route path="/t4" exact component={T4Screen} />
  <Route path="/t2" exact component={T2Screen} />
  <Route path="/t3" exact component={T3Screen} />
  <Route path="/error" exact component={ErrorScreen} />
</BrowserRouter>

It's working, but:

  1. is there a way to do the verification per route (without doing it inside in each screen file)?, because there are a couple new pages coming which will not need to have state param, also error screen should not need state param.

  2. I would like to keep the verification in one place, either in App.tsx or in another file



Solution 1:[1]

Yes. Since it seems you are using react-router-dom v5 you can create a custom Route component that does this check for you and conditionally renders the routed component or a redirect.

Example:

import { Redirect, useLocation } from 'react-router-dom';

const StateParamRoute = props => {
  const { search } = useLocation();
  const urlQuery = new URLSearchParams(search);
  const stateParam = urlQuery.get('state');

  return stateParam ? <Route {...props} /> : <Redirect to="/error?state=1" />;
};

Use the StateParamRoute for the routes you want this check to occur.

<BrowserRouter>
  <Switch>
    <StateParamRoute path="/t1" component={T1Screen} />
    <StateParamRoute path="/t4" component={T4Screen} />
    <StateParamRoute path="/t2" component={T2Screen} />
    <StateParamRoute path="/t3" component={T3Screen} />
    <Route path="/error" component={ErrorScreen} />
    <Route path="/" component={HomeScreen} />
  </Switch>
</BrowserRouter>

Update for react-router-dom@6

Use the useSearchParams hook to access the queryString search, and convert the custom route component into a wrapper component for use in a layout route.

import { Navigate, Outlet, useSearchParams } from 'react-router-dom';

const StateParamWrapper = () => {
  const [search] = useSearchParams();
  const stateParam = search.get('state');

  return stateParam ? <Outlet /> : <Navigate to="/error?state=1" replace />;
};

...

<BrowserRouter>
  <Routes>
    <Route element={<StateParamWrapper />}>
      <Route path="/t1" element={<T1Screen />} />
      <Route path="/t4" element={<T4Screen />} />
      <Route path="/t2" element={<T2Screen />} />
      <Route path="/t3" element={<T3Screen />} />
    </Route>
    <Route path="/error" element={<ErrorScreen />} />
    <Route path="/" element={<HomeScreen />} />
  </Routes>
</BrowserRouter>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1