I know this question has been asked a lot, but I read every question and answer and my problem is not gone.
I'm trying to access http://localhost:3000/1
, and the useParams
should receive 1
to fetch data from an API, but I'm receiving undefined
.
In Component.tsx
, I'm using console.log
to receive the useParams
, but I'm getting undefined
. All related files are posted as I'm using BrowserRouter
correctly and other related React Router imports.
What is wrong with my code? Why am I not getting the correct params?
Component.tsx:
import { createContext, useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { CharacterSchema, PageSchema } from "../interfaces/characterInterfaces";
import { IGetAllCharacters } from "../interfaces/contextInterfaces";
import { IChildren } from "../interfaces/reactInterfaces";
import api from "../services/api";
export const GetAllCharactersContext = createContext<IGetAllCharacters>({} as IGetAllCharacters);
export const GetAllCharactersInfo = ({ children }: IChildren) => {
const [charactersList, setCharactersList] = useState<CharacterSchema[]>([]);
let navigate = useNavigate();
const { page } = useParams();
console.log(page);
useEffect(() => {
api
.get(`/characters?page=${page}`)
.then((res) => {
setCharactersList(res.data.data);
window.localStorage.setItem("lastPage", String(page));
return res;
})
.catch((err) => console.error(err));
}, [page]);
const nextPage = () => {
navigate(`/2`);
};
const prevPage = () => {
navigate(`/1`);
};
return (
<GetAllCharactersContext.Provider value={{ charactersList, nextPage, prevPage }}>
{children}
</GetAllCharactersContext.Provider>
);
};
AllRoutes.tsx:
const AllRoutes = () => {
return (
<Routes>
<Route path="/" element={<Navigate to="/1" />} />
<Route path="/:page" element={<Home />} />
<Route path="*" element={<Home />} />
</Routes>
);
};
export default AllRoutes;
index.tsx:
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import Providers from "./contexts/Providers";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
<React.StrictMode>
<BrowserRouter>
<Providers>
<App />
</Providers>
</BrowserRouter>
</React.StrictMode>
);
Having this <Route path="/:page" element={<Home />} />
will let you consume page
with useParams
only in Home
, the component rendered by Route
for this specific url.
A way to accomplish what you want is to move the fetching part inside Home
. To do so, export as part of the context a function that fetches and updates the state:
// ⚠️ Import what's needed
export const GetAllCharactersContext = createContext<IGetAllCharacters>({} as IGetAllCharacters);
export const GetAllCharactersInfo = ({ children }: IChildren) => {
const [charactersList, setCharactersList] = useState<CharacterSchema[]>([]);
const navigate = useNavigate();
const fetchCharactersList = useCallback((page) => {
api
.get(`/characters?page=${page}`)
.then((res) => {
setCharactersList(res.data.data);
window.localStorage.setItem("lastPage", String(page));
})
.catch((err) => console.error(err));
}, []);
const nextPage = () => {
navigate(`/2`);
};
const prevPage = () => {
navigate(`/1`);
};
return (
<GetAllCharactersContext.Provider value={{ charactersList, fetchCharactersList, nextPage, prevPage }}>
{children}
</GetAllCharactersContext.Provider>
);
};
And move that useEffect
you had in the provider inside Home
. Something like this:
// ⚠️ Import what's needed
export default function Home() {
const { fetchCharactersList, charactersList } = useContext(GetAllCharactersInfo);
const { page } = useParams();
useEffect(() => {
if (!page) return;
fetchCharactersList(page);
}, [page]);
// render data
return <div></div>;
}