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