Doing TicTacToe game. In the intro page with the URL "/" I have a component which picks the value pickIndex and carries to "/game" when clicking either <ButtonMultiplayer /> or <ButtonCPU />. The code works fine unless I manually change the URL to "/game" without submitting pickIndex value. Doing so I get an error "Cannot read properties of null (reading 'pickIndex')". This error happens even when I am playing inside the "/game", then changing the URL to some invalid value (getting 404 error), and then setting the URL back to "/game" - it seems that pickIndex value resets to null..
Below is my component of the Intro page.
export default function IntroView() {
const id = useId();
const [isDisabled, setIsDisabled] = useState(true);
const [pickIndex, setPickIndex] = useState("");
const levelRef = useRef("")
const navigate = useNavigate();
return (
<div className={styles.mainContainer}>
<form>
<div className={styles.pickMarkBox}>
<p>PICK PLAYER 1'S MARK</p>
<div className={styles.buttonBox}>
<ButtonMark
classInput="P1"
value="xButton"
classLabel={styles.player1}
markId={id + 'x'}
isActive={pickIndex === 'x'}
onClick={() => {
setPickIndex('x');
setIsDisabled(false);
}}
/>
<ButtonMark
classInput="P2"
value="zeroButton"
classLabel={styles.player2}
markId={id + 'zero'}
isActive={pickIndex === 'zero'}
onClick={() => {
setPickIndex('zero');
setIsDisabled(false);
}}
/>
</div>
<p>REMEMBER: X GOES FIRST</p>
</div>
< ButtonMultiplayer
value="NEW GAME VS PLAYER"
className={`${styles.gameMode} ${styles.newGameVsPlayer}`}
disabled={isDisabled}
onClick={(e) => {
e.preventDefault()
navigate("/game", { state: { gameMode: 'vsPlayer', pickIndex } });
}}
/>
<ButtonCPU
disabled={isDisabled}
onChange={(e) => {
e.preventDefault()
// setLevel(e.target.value)
levelRef.current = e.target.value
navigate("/game", { state: { gameMode: 'vsCPU', pickIndex, levelRef } })
}}
value={levelRef}
/>
</form >
</div >
);
}
In "/game" I use useLocation to retrieve the param passed from the intro page.
export default function Board(src) {
const mark = useLocation().state.pickIndex;
...
}
Question: how to handle possible errors when the user manually changes the URL into "/game" while on the intro page?
Also is there a reason I should not use useLocation and switch instead to useParams?
I have tried using the following, and this is what I would ideally expect to happen.
if (useLocation().state.pickIndex === null) {
navigate("/")
}
but this does not work. I also tried storing in sessionStorage but no result either.
Keep in mind that location.state is potentially null/undefined, so you often can't just directly access route state properties. When URLs are entered directly there will be no passed route state to access.
Extract the route state first, then check the appropriate state value and issue a back navigation if missing as an intentional side-effect.
Example:
const navigate = useNavigate();
const { state } = useLocation();
const { pickIndex } = state || {};
React.useEffect(() => {
if (!pickIndex) {
navigate("/", { replace: true });
// or navigate(-1) to issue a POP to the previous page
}
}, [navigate, pickIndex]);
...
// Conditionally return early to guard UI if necessary
if (!pickIndex) return null;
...
or
const { state } = useLocation();
const { pickIndex } = state || {};
...
// Conditionally return redirect to home
if (!pickIndex) {
return <Navigate to="/" replace />
};
...
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