Home > Back-end >  Why does "useParams()" return "undefined"? Need help for react router/ optional
Why does "useParams()" return "undefined"? Need help for react router/ optional

Time:04-12

I am new to react and react-router-dom. I need your help to learn to how to get the parameter from the react-router. So I thank you in advance for taking your time on my problem. I am trying to recreate a SPA shop using react. You can find my code at this GitHub link.

These are the dependencies that i am using:

"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.4",
"@testing-library/user-event": "^13.5.0",
"axios": "^0.26.1",
"react": "^18.0.0",
"react-bootstrap": "^2.2.3",
"react-dom": "^18.0.0",
"react-redux": "^7.2.8",
"react-router-bootstrap": "^0.26.1",
"react-router-dom": "^6.3.0",
"react-scripts": "^2.1.3",
"redux": "^4.1.2",
"redux-devtools-extension": "^2.13.9",
"redux-thunk": "^2.4.1",
"web-vitals": "^2.1.4"
}

what I want is to create a shopping cart by adding the object in quantity. By using redux I am trying to redirect my user to the shopping cart. at first, I wanted to use "history" and the optional parameter "?", however, I found that they are not supported in react v6 so I used another hook called "useNavigate". the code in my productScreen.js is as follows:

import React, { useState, useEffect } from "react";
import { Link, useParams, useNavigate } from "react-router-dom";
import {
  Row,
  Col,
  Image,
  ListGroup,
  Card,
  Button,
  Form,
} from "react-bootstrap";
import Rating from "../components/Rating";
import { useDispatch, useSelector } from "react-redux";
import { listProductDetails } from "../actions/productActions";
import Loader from "../components/Loader";
import Message from "../components/Message";

const ProductScreen = () => {
  const [qty, setQty] = useState(1);

  const { id } = useParams();
  const dispatch = useDispatch();
  const productDetails = useSelector((state) => state.productDetails);
  const { loading, error, product } = productDetails;
  useEffect(() => {
    dispatch(listProductDetails(id));
  }, [dispatch, id]);

  const navigate = useNavigate();
  const addToCartHandler = () => {
    navigate(`/cart/${id}?qty=${qty}`);
  };

  return (
    <>
      <Link className="btn btn-dark my-3" to="/">
        Go Back
      </Link>
      {loading ? (
        <Loader />
      ) : error ? (
        <Message variant="danger">{error}</Message>
      ) : (
        <Row>
          <Col ms={6}>
            <Image src={product.image} alt={product.name} fluid />
          </Col>
          <Col md={3}>
            <ListGroup variant="flush">
              <ListGroup.Item>
                <h3>{product.name}</h3>
              </ListGroup.Item>
              <ListGroup.Item>
                <Rating
                  value={product.rating}
                  text={`${product.numReviews} reviews`}
                />
              </ListGroup.Item>
              <ListGroup.Item>Price: ${product.price};</ListGroup.Item>
              <ListGroup.Item>
                Description: ${product.description};
              </ListGroup.Item>
            </ListGroup>
          </Col>
          <Col md={3}>
            <Card>
              <ListGroup variant="flush">
                <ListGroup.Item>
                  <Row>
                    <Col>Price</Col>
                    <Col>
                      <strong>{product.price}</strong>
                    </Col>
                  </Row>
                </ListGroup.Item>
                <ListGroup.Item>
                  <Row>
                    <Col>Status</Col>
                    <Col>
                      {product.countInStock > 0 ? "InStock" : "Out of Stock"}
                    </Col>
                  </Row>
                </ListGroup.Item>

                {product.countInStock > 0 && (
                  <ListGroup.Item>
                    <Row>
                      <Col>Qty</Col>
                      <Col>
                        <Form.Control
                          as="select"
                          value={qty}
                          onChange={(e) => setQty(e.target.value)}
                        >
                          {[...Array(product.countInStock).keys()].map((x) => (
                            <option key={x   1} value={x   1}>
                              {x   1}
                            </option>
                          ))}
                        </Form.Control>
                      </Col>
                    </Row>
                  </ListGroup.Item>
                )}

                <ListGroup.Item>
                  <Button
                    className="btn-block"
                    type="button"
                    disabled={product.countInStock === 0}
                    onClick={addToCartHandler}
                  >
                    Add To Cart
                  </Button>
                </ListGroup.Item>
              </ListGroup>
            </Card>
          </Col>
        </Row>
      )}
    </>
  );
};

export default ProductScreen;

I am using navigate to redirect users to my cart and also bypassing the id parameter and qty as a query string to connect to local storage and the database.

Since optional parameter is not supported in react router v6, I deigned my routers as follow:

<Routes>
  <Route path="/" element={<HomeScreen />} />
  <Route path="/product/:id" element={<ProductScreen />} />
  <Route path="/cart" element={<CartScreen />} />
  <Route path="/cart/:id" element={<CartScreen />} />
</Routes>

In my cartScreen.js file, I can get the query using a hook called "research".On the other hand, when I try to get the id parameter, the "useParam" hook returns undefined.

import React, { useEffect } from "react";
import {
  Link,
  useSearchParams,
  useNavigate,
  useParams,
} from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import Message from "../components/Message";
import {
  Row,
  Col,
  ListGroup,
  Image,
  Form,
  Button,
  Card,
} from "react-bootstrap";
import { addToCart } from "../actions/cartActions";
import { disable } from "colors";

const CartScreen = () => {
  const { productID } = useParams();
  console.log(productID);
  const [qtyString] = useSearchParams();
  const qty = Number(qtyString.toString().split("=")[1]);
  console.log(qty)

  const dispatch = useDispatch();
  useEffect(() => {
    if (productID) {
      dispatch(addToCart(productID, qty));
    }
  }, [dispatch, productID, qty]);
  return <div>CartScreen</div>;
};

export default CartScreen;

Would you mind teaching me how can I get the parameter ("id") from the router? any help would be appreciated. Thank you,

CodePudding user response:

So your issue is on this line

const { productID } = useParams(); <=========

here you have to put the same name you have defined in the Routes which in your case is :id see below

<Route path="/product/:id" element={<ProductScreen />} />

So what you need to do is follows:

const { id } = useParams();

CodePudding user response:

The param here is called id: <Route path="/cart/:id" element={<CartScreen />} />

You got it right for products: const { id } = useParams();

But in cart you've called it productID:

const { productID } = useParams();
console.log(productID);

Change it to:

const { id } = useParams();
console.log(id);
  • Related