The what?
I am making build a React Typescript app that makes calls to https://swapi.dev to render pages of various resources like people
, films
or planets
.
The why?
I need to make sure that the user is not able to manipulate the URL such that they are able to go to /characters
without specifying a pageNumber
param like so: localhost:3000/character?page=1
The how?
As an example I have a route like the one below, where I need to Redirect the user to /characters?page=1
if they enter enter /characters
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route
path="characters"
element={<SwapiResourcePage resourceType={"people"}
/>
<Route
path="characters/:id"
element={<SwapiResourceDetailsPage resourceType={"people"}
/>
</Route>
</Routes>
What have you tried?
I've tried replacing the path to path=characters?page=:id
and setting it to exact, but this is not valid syntax in react-router
. Also had a quick glance at https://reactrouter.com/docs/en/v6/ , but I couldn't find anything relevant.
How can I help ?
I need help figuring out how to enforce searchParams on a react-router route.
CodePudding user response:
You can't do this at the route level since queryString parameters are not part of the path. Only the path is used by react-router-dom
Route
components.
Use the useSearchParams
hook in the matched/routed component and check for the page
queryString parameter and issue an imperative back navigation if it is missing.
Example:
import { useNavigate, useSearchParams } from 'react-router-dom';
const SwapiResourcePage = ({ resourceType }) => {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const page = searchParams.get("page");
useEffect(() => {
if (!page) navigate(-1); // <-- no page param, navigate back
}, [navigate, page]);
...
if (!page) {
return null;
}
// render content
...
Update
It seems like your question was more about providing a fallback page
queryString value than preventing access to routed content if page
value was missing. You can use the same code/logic as above, but instead of using navigate
to issue a back navigation off the route, just update the queryString.
import { useSearchParams } from 'react-router-dom';
const SwapiResourcePage = ({ resourceType }) => {
const [searchParams, setSearchparams] = useSearchParams();
const page = searchParams.get("page");
useEffect(() => {
if (!page) {
// No page param, replace current
searchParams.set("page", 1);
setSearchparams(searchParams, { replace });
}
}, [searchParams, setSearchparams, page]);
...
if (!page) {
return null;
}
// render content
...
CodePudding user response:
So, I ended up solving this by creating an intermediary component called <ValidateQueryParams/>
, here's the code I used for those who are interested:
import React, { useEffect } from "react";
import { useSearchParams } from "react-router-dom";
import validatePageNumber from "../helpers/validatePageNumber";
import SwapiResourcePage from "../pages/SwapiResourcePage";
interface ValidateQueryParamProps {
resourceType: string;
}
const ValidateQueryParam: React.FC<ValidateQueryParamProps> = ({
resourceType,
}) => {
const [searchParams, setSearchParams] = useSearchParams();
const pageNumber = searchParams.get("page");
useEffect(() => {
if (
pageNumber == null ||
!pageNumber.match(/\d /) ||
!validatePageNumber(resourceType, parseInt(pageNumber))
) {
setSearchParams({ page: "1" });
}
}, []);
return <SwapiResourcePage resourceType={resourceType} />;
};
export default ValidateQueryParam;
<Routes>
<Route path="/" element={<Layout />}>
<Route index element={<Home />} />
<Route
path="people"
element={<ValidateQueryParam resourceType={"people"} />}
/>
<Route
path="people/:id"
element={<SwapiResourceDetailsPage resourceType={"people"} />}
/>
</Route>
</Routes>