Initially, it works if the manually input link is wrong (Ex: "/characters/test"
), but if it’s the correct one, it still redirects to error 404. If the link is clicked from Character
component, it works normally. Which means no matter what link I put manually on the browser, it results in error 404.
I’ve been trying to implement an error 404 redirect on a CharacterCard
component by comparing the id being passed – ‘name’ (as useParams
) and comparing it to an characterId list from another component being passed, if it matches, then it renders normally, if not, it redirects to error 404.
Since I’m using React-Router v6, I can’t pass props normally as far as I know. After a bit of research, I found out I can use Link
state to pass data to another component it links to and useLocation
to get the passed data. I successfully passed data to the component, but then it results to the problem I stated above.
App.js (Route Declarations)
import { Route, Routes } from 'react-router-dom';
import './App.css';
import { Nations } from './components/Nations';
import { Navbar } from './components/Navbar';
import { Characters } from './components/Characters';
import { Home } from './components/Home';
import { Artifacts } from './components/Artifacts';
import { Weapons } from './components/Weapons';
import { CharacterCard } from './components/CharacterCard';
import { NotFound } from './components/NotFound';
function App() {
return (
<div className='bg-[#1e1f21]'>
<Navbar />
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/nations' element={<Nations/>}/>
<Route path='/weapons' element={<Weapons/>}/>
<Route path='/characters' element={<Characters/>}/>
<Route path='/characters/:name' element={<CharacterCard/>}/>
<Route path='/artifacts' element={<Artifacts/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
</div>
);
}
export default App;
Characters
import { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { PuffLoader } from "react-spinners";
const API_URL = 'something.somethin.com';
const CDN_URL = 'something2.somethin2.com';
export const Characters = () => {
const [characters, setCharacters] = useState([]);
const [isLoaded, setIsLoaded] = useState(false);
const validCharacterIds = characters.map((character) => character.id);
console.log('valid ids:', validCharacterIds);
const getCharacters = async () => {
try {
const response = await fetch({API_URL});
const charactersData = await response.json();
const characterDetailsData = await Promise.all(
charactersData.map(async (character) => {
const characterDetails = await getCharacterDetails(character);
return characterDetails;
})
);
setCharacters(characterDetailsData);
console.log('characters: ', characterDetailsData);
} catch (error) {
console.log("Error fetching Characters:", error);
}
};
const getCharacterDetails = async (character) => {
//code for getCharacterDetails
setIsLoaded(true);
return { ...characterDetailsData, id:character, icon: imageData };
};
useEffect(() => {
getCharacters();
document.title = "Characters | Website Title"
}, []);
return (
<div className='w-full py-[3rem] px-4 bg-white'>
//Character Card that when clicked opens CharacterCard Component
<div className='w-[500px] md:w-[1200px] mx-auto grid grid-cols-1 md:grid-cols-5 gap-5'>
{isLoaded ? (
(characters.length > 0) ? (
characters.map((character) => (
<Link key={character.name} to={`/characters/${character.id}`} state={{charIds: validCharacterIds}} className='w-full shadow-xl flex flex-col my-4 rounded-lg hover:scale-105 duration-300'>
<div className={
character.rarity === 4
? "bg-gradient-to-b from-[#5e5789] to-[#9c75b7]"
: character.rarity === 5
? "bg-gradient-to-b from-[#945c2c] to-[#b27330]"
: ''
}>
<img className='w-[300px] h-[250px] object-cover' src={character.icon} alt={character.name + ' Icon'} />
</div>
<div>
<h4 className='text-2xl font-bold text-center py-8'>{character.name}</h4>
</div>
</Link>
))
) : (
<div className='empty'>
<h2>No Characters Found!</h2>
</div>
)
) : (
<div className="flex justify-center items-center">
<PuffLoader color="#36d7b7" />
</div>
)}
</div>
</div>
);
};
CharacterCard
import React, { useEffect, useState } from 'react';
import { useNavigate, Navigate, useParams, useLocation } from 'react-router-dom';
import { PuffLoader } from 'react-spinners';
import Carousel from './Carousel';
const CDN_URL = 'somethin.somethin.com';
const API_URL = 'somethin2.somethin2.com';
export const CharacterCard = () => {
const [characterDetails, setCharacterDetails] = useState(null);
const navigate = useNavigate();
const { name } = useParams();
const location = useLocation();
let validCharIds = location.state;
console.log(location);
const getCharacterDetails = async (characterName) => {
//rest of the api fetch goes here
};
useEffect(() => {
const checkValidCharId = async () => {
if (!validCharIds || !validCharIds.charIds || !validCharIds.charIds.includes(name)) {
// Redirect to error 404 if the name is not in the validCharIds array or empty
navigate('*');
console.log('error 404');
} else {
// Fetch character details only if the name is valid
await getCharacterDetails(name);
}
};
checkValidCharId();
}, [name, validCharIds, navigate]);
if(characterDetails) {
return (
//Render component normally
);
}
else {
return (
<div className='flex justify-center items-center'>
<Navigate to='*'/>
</div>
)
}
};
2
Answers
Route path parameters are always a string type. I suspect the
character.id
is a non-string value and gets stringified when passed as thename
route path parameter.The recommendation is to do a type insensitive comparison. String to String is common since it covers non-number strings, e.g.
"0123456789abcdef"
, and number-like strings, e.g.5
vs"5"
.The issue you’re facing might be related to the way you’re using the state prop in the Link component. Instead of relying on the state prop of the Link component, you can use a different approach to pass data to the CharacterCard component. You can update the to prop of the Link component to include the character id as a parameter in the route:
This way, you’re passing the charIds as a query parameter in the URL. Then, in the CharacterCard component, you can retrieve this query parameter using useParams:
This way, whether the link is clicked or manually inputted, the necessary data should be available in the CharacterCard component, and the 404 redirection logic should work as expected.