http-redirectreact-router-domreactivesearch

React Router conditional redirect


I'm building a search UI using React, React Router and the awesome Reactivesearch library. I'm trying to figure out how I can prevent users from simply navigating to mydomain.com/search, since that is my search results route.

Ideally, if users tried to navigate to mydomain.com/search, I will use RR Redirect component to redirect to the home page.

I'm using "/search" for the route that the Route component in RR(v5) to render the search results page and can't quite figure out how to use something like /search?q=${value} to render the page?

As a preface I do have this in the render block (I'm using class based component for search results)

let value = JSON.stringify(queryString.parse(location.search));
if (this.value === '' || null) {
       return (
         <Redirect to="/" />
       );
     }

However, its not working... I can still go to my address bar and type in mydomain.com/search and the page renders.

Here is an example in my SearchResults.tsx:

<Route path = "/search" render={() => (
   <ReactiveList
    ...
    ...
    />
   />

I'm trying to get to

<Route path = `/search?q="${value}"` render={() => (
   <ReactiveList
    ...
    ...
    />
   />

Update Docs on ReactiveList Example from docs:

<ReactiveList
    componentId="SearchResult"
    dataField="ratings"
    pagination={false}
    paginationAt="bottom"
    pages={5}
    sortBy="desc"
    size={10}
    loader="Loading Results.."
    showResultStats={true}
    renderItem={res => <div>{res.title}</div>}
    renderResultStats={function(stats) {
        return `Showing ${stats.displayedResults} of total ${stats.numberOfResults} in ${
            stats.time
        } ms`;
    }}
    react={{
        and: ['CitySensor', 'SearchSensor'],
    }}
/>

How can I prevent users from simply navigating to mydomain.com/search ??


Solution

  • If you want to conditionally render the ReactiveList component based on if there's a truthy q queryString parameter then you can use either a wrapper component, a layout route, or a Higher Order Component (HOC) to read the queryString and handle the redirection logic.

    react-router-dom@6

    Using a Wrapper

    import { Navigate, useSearchParams } from 'react-router-dom';
    
    const QuaryParametersWrapper = ({ children, parameters = [] }) => {
      const [searchParams] = useSearchParams();
    
      const hasParameter = parameters.some(
        (parameter) => !!searchParams.get(parameter)
      );
    
      return hasParameter ? children : <Navigate to="/" replace />;
    };
    

    ...

    <Route
      path="/search"
      element={(
        <ReactiveListWrapper parameters={["q"]}>
          <ReactiveList
            ...
            ...
          />
        </ReactiveListWrapper>
      )}
    />
    

    Using a custom HOC

    import { Navigate, useSearchParams } from 'react-router-dom';
    
    const withQueryParameters = (...parameters) => (Component) => (props) => {
      const [searchParams] = useSearchParams();
    
      const hasParameter = parameters.some(
        (parameter) => !!searchParams.get(parameter)
      );
    
      return hasParameter ? <Component {...props} /> : <Navigate to="/" replace />;
    };
    

    ...

    export default withQueryParameters("q")(ReactiveList);
    

    ...

    import ReactiveListWrapper from '../path/to/ReactiveList';
    
    ...
    
    <Route path="/search" element={<ReactiveListWrapper />} />
    

    Using a layout route

    import { Navigate, Outlet, useSearchParams } from 'react-router-dom';
    
    const QuaryParametersLayout = ({ parameters = [] }) => {
      const [searchParams] = useSearchParams();
    
      const hasParameter = parameters.some(
        (parameter) => !!searchParams.get(parameter)
      );
    
      return hasParameter ? <Outlet /> : <Navigate to="/" replace />;
    };
    

    ...

    <Route element={<QuaryParametersLayout parameters={["q"]} />}>
      <Route path="/search" element={<ReactiveList />} />
    </Route>
    

    Demo

    Edit react-router-conditional-redirect

    react-router-dom@5

    The useSearchParams hook doesn't exist in v5 so you can create your own.

    import { useMemo } from 'react';
    import { useLocation } from 'react-router-dom';
    
    const useSearchParams = () => {
      const { search } = useLocation();
      const searchParams = useMemo(() => new URLSearchParams(search), [search]);
      return [searchParams];
    };
    

    Using a Wrapper

    import useSearchParams from '../path/to/useSearchParams';
    
    const QuaryParametersWrapper = ({ children, parameters = [] }) => {
      const [searchParams] = useSearchParams();
    
      const hasParameter = parameters.some(
        (parameter) => !!searchParams.get(parameter)
      );
    
      return hasParameter ? children : <Redirect to="/" />;
    };
    

    ...

    <Route
      path="/search1"
      render={(routeProps) => (
        <QuaryParametersWrapper parameters={["q"]}>
          <ReactiveList {...routeProps} />
        </QuaryParametersWrapper>
      )}
    />
    

    Using a custom HOC

    import { Redirect } from 'react-router-dom';
    import useSearchParams from '../path/to/useSearchParams';
    
    const withQueryParameters = (...parameters) => (Component) => (props) => {
      const [searchParams] = useSearchParams();
    
      const hasParameter = parameters.some(
        (parameter) => !!searchParams.get(parameter)
      );
    
      return hasParameter ? <Component {...props} /> : <Redirect to="/" />;
    };
    

    ...

    export default withQueryParameters("q")(ReactiveList);
    

    ...

    import ReactiveListWrapper from '../path/to/ReactiveList';
    
    ...
    
    <Route path="/search2" component={QueryReactiveList} />
    

    Demo

    Edit react-router-conditional-redirect (RRDv5)