Search code examples
reactjsreact-reduxreact-routersingle-page-applicationurl-routing

Data fetching not happening when I use two parameters in React Router Dom and navigate directly on the page


I faced a really strange and complex problem with React Router. I am trying to implement a single page application with a varying number of pages that depends on the number of products in the json file. In my app I have the main page that always consist only of one pages thus it has only one paramater which is called :section. The bug I'm stuck with happens only when I use the second parameter :page_number, all other pages have this parameter.

This is my main component that contains various pages and depends on the router params

import { Routes, Route } from 'react-router-dom';
import GoodsList from './GoodsList';
import Pages from './Pages';

function Sections() {
    return (
        <Routes>
            <Route path='' element={<>
                <div className="pageContent" data-main="true">
                    <p>Novelties</p>
                    <GoodsList goods="novelties" />
                    <p>Hot</p>
                    <GoodsList goods="hot" />
                </div>
            </>} />
            <Route path='/:section/:page_number' element={<>
                <div className='pageContent'>
                    <GoodsList />
                    <Pages />
                </div>
            </>} />
        </Routes>
    )

This component I use for navigation on the page

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

function Menu() {
    return (
        <nav className='menu'>
            <ul>
                <li>
                    <Link to="">Main</Link>
                </li>
                <li>
                    <Link to="figures/1">Figures</Link>
                </li>
                <li>
                    <Link to="dakimakuras/1">Figures</Link>
                </li>
                <li>
                    <Link to="manga/1">Manga</Link>
                </li>
                <li>
                    <Link to="other/1">Other merch</Link>
                </li>
            </ul>
        </nav>
    )
}

And finaly this component displays the list of products

import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchProducts } from '../redux/fetchDataReducer';
import { useParams } from 'react-router-dom';

function GoodsList(props) {
    const params = useParams();
    const goods = returnGoods();
    function returnGoods() {
        if (document.location.pathname == "/") {
            return props.goods
        }
        return params.section
    }
    
    const dispatch = useDispatch();
    useEffect(() => {
        dispatch(fetchProducts());
    }, [])
    
    const products = useSelector(state => state.fetchDataReducer.products);
    
    return (
        <div className="list">
            {products && products[goods].map((good, i) => (
                <div className="productCard" key={i}>
                    <div className="productPicture">
                        <img src={good.pic} alt="ERROR" />
                    </div>
                    <p className="productName">{good.name}</p>
                </div>
            ))}
        </div>
    )
}

The thing is when I click on the main page in the nav bar or move to it directly by typing the path in the address bar it always works fine. Then if I click on any section in the nav bar everything works fine either, BUT if I will try to refresh the page or move to any of the pages except the main by directly typing its address in the bar the GoodsList component won't load the products list. The fetching of data won't happen somehow, but the component itself and all the parameters from the address bar will be loaded fine. BUT if I will move to the main page again and then to any other section by using the nav Link it will be loaded fine again and the problem won't appear till I refresh the page.

So, the problem appears only if I have the :page_number parameter and affects only the data I recieve from json file through Redux. If I delete this second parameter the bug won't happen. It happens when I move to any section with this parameter directly or refresh the page, if I use navigation links on my menu bar it works fine but ONLY if I visited the main page before that.

UPD. Adding the pages component

import { useSelector } from "react-redux";
import { Link, useParams } from 'react-router-dom';

function Pages() {
    const params = useParams();
    const products = useSelector(state => state.fetchDataReducer.products);
    const currentProduct = products ? products[params.section] : [];
    const pageLinks = [];

    let numberOfPages = () => {
        if ((currentProduct.length / 8) % 1 == 0) {
            return currentProduct.length / 8
        }
        else return Math.floor((currentProduct.length / 8) + 1)
    }

    for (let x = 1; x < (numberOfPages() + 1); x++) {
        pageLinks.push(<Link to={`/${params.section}/${x}`} className="pageLink" key={x}>{x}</Link>);
    }
    
    return (
        <div className="pageList">
            {pageLinks}
        </div>
    )
}

UPD. Codesandbox example https://codesandbox.io/p/github/TheDanieLSh/React-Sites/draft/modest-hopper?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522panelType%2522%253A%2522TABS%2522%252C%2522id%2522%253A%2522cli1ipbjf00iu356lorpxfmbk%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522cli1ipbjf00iu356lorpxfmbk%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522cli1ipbje00it356lhcqi48gt%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252F.gitignore%2522%257D%255D%252C%2522id%2522%253A%2522cli1ipbjf00iu356lorpxfmbk%2522%252C%2522activeTabId%2522%253A%2522cli1ipbje00it356lhcqi48gt%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D


Solution

  • The fetchProducts action is using a relative resource path for the products.json file it's fetching/loading from the "/public" directory.

    fetch("products.json")
    

    This works fine and as expected when the app is currently sitting on the root "/" route rendering the GoodsList component when is dispatches fetchProducts. The products.json resource is requested relative to the current URL path, e.g. "/products.json" and the data is loaded into the Redux store without issue.

    Upon navigating to the sub-route, e.g. "/:section/:page_number", the products.json file is now requested relative to that path, e.g. "/figures/1/products" which doesn't exist in the public directory. When the user is on a sub-route and reloads the page, the fetch just fails. This is fine so long as the user at least (eventually) lands on the main "/" page so when the request is made it actually succeeds.

    To resolve you can simply specify an absolute resource path to "/products.json".

    fetch("/products.json")
    
    export const fetchProducts = createAsyncThunk(
      "dataFetch/fetchProducts",
      async () => {
        const response = await fetch("/products.json");
        const data = await response.json();
        return data;
      }
    );
    

    Edit data-fetching-not-happening-when-i-use-two-parameters-in-react-router-dom-and-na