I have a small issue I'm not able to fix. In my react app I use react-router v6 and I have to following routes:
<Route path="/" element={<App />} />
<Route path=":id" element={<CharacterDetails/>} />
<Route path="*" element={<Navigate replace to="/" />} />
As you can see, I have a route that needs an id. This works fine as long as I provide an existing Id so that CharacterDetails component fetches some data successfully. However, if I pass some random text in the URL like "localhost:3000/randomText" the CharacterDetails component still shows for a brief second till the useEffect fires to determine that the request is a 404 and only after that it redirects me to the App component.
How can I check if the URL provided should indeed return some data before rendering the component ? and redirect to the App component directly (without the flickering of the CharacterDetails component) when it is not a valid URL
Thanks!
EDIT: I'm not sure if this is a router issue or should I do it at the component level, I'm waiting for suggestions
EDIT2: Component code
const CharacterDetails = () => {
const { id } = useParams<string>();
const navigate = useNavigate();
const [state, dispatch] = useReducer(characterReducer, initialState);
const { data, episodes, loading } = state;
useEffect(() => {
const fetchData = async (id: string) => {
dispatch({ type: "LOADING_START" })
try {
let response = await getSingleCharacterData(id);
let URLs = response.data.episode;
let listOfEpisodes = await getEpisodeName(URLs);
dispatch({
type: "FETCH_SUCCESS",
payload: { data: response.data, episodeList: listOfEpisodes },
});
dispatch({ type: "LOADING_OVER" })
} catch (error) {
dispatch({ type: "LOADING_OVER" })
navigate("/");
}
};
if (id) fetchData(id);
}, [id, navigate]);
return (
<CharacterDetailsContainer>
{loading ? <Loading /> :
data && (
<div> </div>
)}
</CharacterDetailsContainer>
}
This isn't an issue with the router/routes, it's something that routed components need to handle.
In the CharacterDetails
component use some "loading" state to conditionally render null
or some loading indicator while the id
path param is validated. Note that "loading" needs to be the initial state so the code isn't leaking any initial non-loading UI, waiting until the useEffect
hook runs at the end of the initial render to dispatch({ type: "LOADING_START" })
is too late unless the initial redux state has it set true.
Example:
const CharacterDetails = () => {
const { id } = useParams();
const navigate = useNavigate();
const [isLoading, setIsLoading] = React.useState(true);
useEffect(() => {
setIsLoading(true);
// logic to validate id param
if (is404) {
navigate("/404", { replace: true }); // redirect
} else {
setIsLoading(false); // clear loading state so page content renders
}
}, [id]);
if (isLoading) {
return null; // or loading spinner/etc...
}
return page content
};
Your code:
const CharacterDetails = () => {
const { id } = useParams<string>();
const navigate = useNavigate();
const [isLoading, setIsLoading] = React.useState<boolean>(true); // <-- initially true
const [state, dispatch] = useReducer(characterReducer, initialState);
const { data, episodes } = state;
useEffect(() => {
const fetchData = async (id: string) => {
setIsLoading(true);
dispatch({ type: "LOADING_START" });
try {
let response = await getSingleCharacterData(id);
let URLs = response.data.episode;
let listOfEpisodes = await getEpisodeName(URLs);
dispatch({
type: "FETCH_SUCCESS",
payload: { data: response.data, episodeList: listOfEpisodes },
});
setIsLoading(false);
} catch (error) {
// handle any errors, etc...
// redirect home
navigate("/", { replace: true });
} finally {
dispatch({ type: "LOADING_OVER" });
}
};
if (id) fetchData(id);
}, [id, navigate]);
if (isLoading) {
return null; // or loading spinner/etc...
}
return ( <some JSX> )
}
You can use the useParams hook in the child.
const acceptableIDs = ["dog", "cat"];
function CharacterDetails() {
let { id } = useParams();
return acceptableIDs.includes(id) ? (
<div>
<h3>ID: {id}</h3>
</div>
) : null; // render nothing or redirect
}
If it takes too long to check if the ID is valid, you could show a transition.
Note this is business logic and should probably not bleed into the router.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With