'Why React-router doesn't re-render the page and doesn't update the data when the url changes?

I'm building a project in React that retrieves data from an API through a custom hook, as soon as the data is retrieved, it shows cards that by clicking on them open the descriptive page. Up to here everything is ok.

// App.js
<Routes>
   <Route path="/" element={<Home />} /> // cards list
   <Route path="/:eleId" element={<Details />} /> // description page 
</Routes>

// Home.jsx with single card
<div className="container">
   <Link to={`/${eleId}`}>
      <Card />
   </Link>
</div>

My problem comes here: inside the description page I have related links that open the same page when clicked but changing the data:

// Details.jsx description page 
import { useParams, Link } from 'react-router-dom';
import useData from '../hooks/useData';

const Details = () => {
   const { eleId } = useParams(),
         [data] = useData(`alpha/${eleId}`); // call API with url of eleId

   return(
      <div className="description">
         <p>BLa bla bla</p>
         <Link to={`/${data.id}`}> Related </Link>
      </div>
   );
}

export default Details;
// useData.jsx custom hook
import { useState, useEffect } from 'react';

const useData = (param = 'all') => {
   const [data, setData] = useState([]),
         [error, setError] = useState(null),
         [isLoading, setIsLoading] = useState(true);
    
    const url = 'https://restcountries.com/v2/'+param;

    useEffect(() => {
        const fetchAPI = async () => {
            setIsLoading(true);
            try {
                const response = await fetch(url),
                        json = await response.json();
                setData(json);
                setError(false);
            } catch(err) {
                setError(err.message);
                setData(null);
                console.log(err);
            }
            setIsLoading(false);
        };
        fetchAPI(); 
    }, []);

   return [data, error, isLoading];
}

export default useData;

the url changes correctly but the data does not update ... but if I refesh it updates.

I don't understand why React can't re-render the page. Can anyone give me some suggestions?

Thank you :)



Solution 1:[1]

// Details.jsx description page 
<div className="description">
  <p>BLa bla bla</p>
  <Link to={`/${eleId}`}>Related</Link>
<div>

If Details component is linking to itself, i.e. the route rendering it, this means the route path is already matched and rendering Details. Details may need to "listen" for changes on the eleId route match param. Use the useParams React hook to access the eleId param and an useEffect hook with eleId as a dependency to run any side-effect necessary when the param updates.

Example:

import { useParams } from 'react-router-dom';

...

const Details = () => {

  const { eleId } = useParams();
  const [data] = useData(`alpha/${eleId}`);

  ...

  useEffect(() => {
    // Business logic to run when eleId updates
  }, [eleId]);

  ...

For the useData hook, move the url declaration into the useEffect hook and add the param argument as a dependency for the effect. When the param changes this will retrigger the useEffect and refetch data.

const useData = (param = 'all') => {
  const [data, setData] = useState([]);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
    
  useEffect(() => {
    const url = 'https://restcountries.com/v2/' + param;

    const fetchAPI = async () => {
      setIsLoading(true);
      try {
        const response = await fetch(url);
        const json = await response.json();
        setData(json);
        setError(false);
      } catch(err) {
        setError(err.message);
        setData(null);
        console.log(err);
      }
      setIsLoading(false);
    };
    fetchAPI(); 
  }, [param]);

  return [data, error, isLoading];
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1