1

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 ??

user3125823
  • 1,846
  • 2
  • 18
  • 46
  • `react-router-dom` route components deal only with the URL path for route matching and rendering. You'll need to ready any queryString parameters in the routed component and handle any redirection there. Can you update your question to include a more complete and comprehensive code example ([mcve]), specifically this `ReactiveList` component? – Drew Reese Jul 07 '22 at 20:25
  • @DrewReese so it sounds like your saying I may need to implement something like a Result HOC that would check for query string params and anything else that needs to be present in order for that route to render? I also added an update about ReactiveList – user3125823 Jul 08 '22 at 21:28
  • A HOC might work, but I was thinking something more along the lines of a wrapper component, or just checking directly in the component itself. Is `ReactiveList` a 3rd-party component, i.e. something you don't own? If this is the case, then yes, you'd need to wrap/decorate it with a component that can handle the logic of reading any query params and act accordingly. You just want to only render the `ReactiveList` component if there is a `q` query param, otherwise redirect somewhere or issue a back navigation? Would you like examples? – Drew Reese Jul 08 '22 at 21:29
  • @DrewReese Yes and no with ReactiveList. Its part of a 3rd party search UI lib that I use. It is highly configurable but ultimately it interacts with a 3rd party search api that I don't own. Yes I would love an example where there is a q query param present, otherwise direct back home or back navigation!!! – user3125823 Jul 09 '22 at 22:31

1 Answers1

0

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)

Drew Reese
  • 165,259
  • 14
  • 153
  • 181
  • Thanks! I'm just now getting to it and will accept it as soon as I have it working. I'm still am on RR v5 so I'm guessing I can replace element with component and Navigate with Redirect? – user3125823 Jul 12 '22 at 16:07
  • @user3125823 Oh, my bad... totally missed the fact that you are using RRDv5. I'll update with a v5 version of this answer. – Drew Reese Jul 15 '22 at 02:15
  • really appreciate the detailed answer. I worked on this problem for probably a week or so until I fully understood it and was then able to use your HOC code to solve it. Up until now I hadn't fully understood the whole query params deal with RR - now I do!! – user3125823 Jul 30 '22 at 19:05
  • @user3125823 Sure thing, glad to help! Cheers and good luck! – Drew Reese Jul 31 '22 at 02:20