Home > Back-end >  React Router not rerendering page after it updates URL (UseNavigate or Link)
React Router not rerendering page after it updates URL (UseNavigate or Link)

Time:11-04

I have a shopping site built with React and React Router, There is a main shopping page and a product page for each product, the product page has a next page button that calls

const handleNext = () => {
  navigate(`/shop/product/${product.id   1}`, { replace: true })
}

The URL changes but the page does not rerender and stays on the same product, if I refresh the page manually the page does update.

I have also tried using and it does the same thing

<Link to=`/shop/product/${product.id   1}`>Next</Link> 

Although when I reroute to '/home' or '/contact' it works.

RouteSwitch.JS

import { BrowserRouter, Routes, Route } from "react-router-dom";
import HomePage from "./components/Homepage/Homepage";
import ShoppingPage from "./components/ShoppingPage/ShoppingPage";
import Nav from "./components/Nav/Nav";
import Contact from "./components/Contact/Contact";
import ProductPage from "./components/ProductPage/ProductPage";
import Checkout from "./components/Checkout/Checkout";

const RouteSwitch = (props) => {
  const { cart, setCart } = props.props;

  return (
    <BrowserRouter>
      <Nav cart={cart} />
      <Routes>
        <Route exact path="/" element={<HomePage />} />
        <Route path="/shop" element={<ShoppingPage />} />

        <Route
          exact
          path="/shop/product/:id"
          element={<ProductPage props={{ setCart, cart }} />}
        />

        <Route path="/contact" element={<Contact />} />
        <Route
          path="/checkout"
          element={<Checkout cart={cart} setCart={setCart} />}
        />
      </Routes>
    </BrowserRouter>
  );
};

export default RouteSwitch;

ProductPage.js

import { useLocation, useParams, useNavigate, Link } from "react-router-dom";
import { useEffect, useState } from "react";
import './ProductPage.css'
import data from "../toy-api/data"
import ProductMenu from "../ProductMenu/ProductMenu"

const ProductPage = (props) => {
  const { setCart, cart } = props.props;

  const getProdByID = (id) => data.find((item) => item.id === parseInt(id));
  const navigate = useNavigate();
  const params = useParams();
  const location = useLocation();
  const [product, setProduct] = useState({});
  const { fromShopRoute = false, prodProps = errorObj } = location.state || {};

  // Use Effect that runs when the COMP mounts
  useEffect((e) => {

      let _product = getProdByID(params.id);

      if (_product !== undefined) {
        setProduct(_product);
      }
      // If the search returns undefined it will set the Product
      // to an errorObj and reroute the user to shop page
      else {
        setProduct(errorObj);
        navigate("/shop");
      }
    }
  }, []);


  const handleNext = () => {
    navigate(`/shop/product/${product.id   1}`, { replace: true })
  }

  return (
    <div>
      <div className="product-page-container">

        <div className="product-header">
          <h1>{product.name}</h1>
          <ProductMenu
            setCart={setCart}
            product={product} 
            setProduct={setProduct}
            cart={cart}
          >
          </ProductMenu>
        </div>


        <div className="img-container">
 
          <img
            className="product-img"
            loading="lazy"
            src={product.src}
            alt={product.name}
          />

          <div onClick={handleNext} className="slide-btn ">
            Next
          </div>

        </div>



      </div>

    </div >
  );
};

export default ProductPage;

I tried all sorts of "UseNavigate(0)" and other methods with useEffect watching the params.id but I have found that they are messy solutions that aren't giving me my required goal.

CodePudding user response:

The ProductPage remains mounted when the "/shop/product/:id" route path is matched and only the id path parameter is changing.

The useEffect hook for fetching data depends on the id route path param.

const { id } = useParams();

// Use Effect that runs on initial render and when id updates
useEffect(() => {
  let _product = getProdByID(id);

  if (_product !== undefined) {
    setProduct(_product);
  } else {
    // If the search returns undefined it will set the Product
    // to an errorObj and reroute the user to shop page
    setProduct(errorObj);
    navigate("/shop");
  }
}, [id]); // <-- add id dependency
  • Related