جاوااسکریپت - چرا useParams حتی با سینتکس صحیح مقدار undefined برمیگرداند؟


میدانم این سوال خیلی پرسیده شده است، اما من هر سوال و پاسخی را خواندهام و مشکل من حل نشده است.

من در تلاش برای دسترسی به http://localhost:3000/1 هستم، و useParams باید 1 را دریافت کند تا دادهها را از یک API دریافت کند، اما من undefined دریافت میکنم.

در Component.tsx، من از console.log برای دریافت useParams استفاده میکنم، اما مقدار undefined دریافت میکنم. همه فایلهای مرتبط ارسال شدهاند چرا که من BrowserRouter را بهدرستی استفاده میکنم و سایر واردات مرتبط با React Router نیز هستند.

مشکل کد من چیست؟ چرا پارامترهای درست را دریافت نمیکنم؟

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>
    );

پاسخ شماره ۱

داشتن این <Route path="/:page" element={<Home />} /> به شما اجازه میدهد که page را با useParams تنها در Home استفاده کنید، کامپوننتی که توسط Route برای این URL خاص رندر میشود.

یک روش برای دستیابی به آنچه میخواهید این است که بخش واکشی را داخل Home منتقل کنید. برای انجام این کار، یک تابع که دادهها را واکشی و حالت را بهروزرسانی میکند به عنوان بخشی از کانتکست صادر کنید:

// ⚠️ 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>
  );
};

و آن useEffect که در Provider داشتید را داخل Home منتقل کنید. چیزی شبیه به این:

// ⚠️ 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>;
}

پاسخ شماره ۲

تازه خودم این را تست کردم. useParams باید داخل <Route> استفاده شود جایی که پارامتر مشخص شده است. این پارامترها را بهطور خودکار از URL نمیگیرد. اگر میخواهید ساختار را حفظ کنید، پیشنهاد میکنم یک تنظیمکننده صفحه از کانتکست خود ارائه دهید و آن را داخل یکی از کامپوننتهایی که میتوانند به این هوک دسترسی داشته باشند تنظیم کنید.

به طور جایگزین میتوانید از useSearchParams استفاده کنید که در واقع دادهها را از پارامترهای جستجوی URL میگیرد، اما در این صورت نمیتوانید آن را در URL داشته باشید.