skip to Main Content

I am not able to fetch the single product details through Redux-Toolkit (RTK) Query but get all products is working but not for single product.

enter image description here
enter image description here

constants.js:

export const BASE_URL='/';
export const PRODUCTS_URL='api/products/';
export const ORDERS_URL='api/orders';
export const USERS_URL='api/users';
export const PAYPAL_URL='api/config/paypal';

useGetProductsQuery is fetching all the products from the api but the useGetProductDetailsQuery is not fetching single product details

Store:

import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit';
import { apiSlice } from './slices/apiSlice';

const store = configureStore({
  reducer: {
    [apiSlice.reducerPath]: apiSlice.reducer
  },
  middleware:(getDefaultMiddleware) => getDefaultMiddleware().concat(apiSlice.middleware),
  devTools: true
})

export default store;

apiSlice:

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { BASE_URL, PRODUCTS_URL } from '../constants';

const baseQuery = fetchBaseQuery({ baseUrl: BASE_URL });

export const apiSlice = createApi({
  baseQuery: baseQuery,
  tagTypes: ["Product", "Order","User"],
  endpoints: (builder) => ({
    getProducts: builder.query({
      query: () => ({
        url: PRODUCTS_URL,
      }),
      keepUnusedDataFor: 5
    }),
    getProductDetails: builder.query({
      query: (productId) => ({
        url: `${PRODUCTS_URL}/${productId}`, // this url is not hitting the api and not fetching the data, but in postman its working fine
      }),
      keepUnusedDataFor: 5
    }),
  })
});

export const {
  useGetProductDetailsQuery,
  useGetProductsQuery
} = apiSlice;

Product Screen:

import { Link, useParams } from "react-router-dom";
import { Col, Row, Image, ListGroup, Card, Button } from "react-bootstrap";
import Rating from "../components/Rating";
import { useGetProductDetailsQuery } from "../slices/apiSlice";

const ProductScreen = () => {
  const { id: productId } = useParams();
  console.log(useGetProductDetailsQuery(productId))
  const { data: product, isLoading, Error } = useGetProductDetailsQuery(productId);

  return (
    <>
      <Link className="btn btn-light my-3" to="/">Go Back</Link>
      <Row>
        <Col md={5}>
          <Image src={product.image} alt={product.name} fluid />
        </Col>
        <Col md={4}>
          <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>
            <ListGroup variant="flush">
              <ListGroup.Item>
                <Row>
                  <Col>Status:</Col>
                  <Col>
                    <strong>{product.countInStock>0?"In Stock":"Out of Stock"}</strong>
                  </Col>
                </Row>
              </ListGroup.Item>
              <ListGroup.Item>
                <Button type="button" className="btn-block" disabled={product.countInStock===0}>
                  Add To Cart
                </Button>
              </ListGroup.Item>
            </ListGroup>
          </Card>
        </Col>
      </Row>
    </>
  )
}

export default ProductScreen;

package.json:

{
  "name": "frontend",
  "version": "0.1.0",
  "proxy": "http://localhost:5000",
  "private": true,
  "dependencies": {
    "@reduxjs/toolkit": "^1.9.5",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.4.0",
    "bootstrap": "^5.3.0",
    "bootstrap-icons": "^1.10.5",
    "react": "^18.2.0",
    "react-bootstrap": "^2.8.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.10.1",
    "react-redux": "^8.1.2",
    "react-router-bootstrap": "^0.26.2",
    "react-router-dom": "^6.14.2",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

I have also added the proxy. I don’t know why its not getting the product details.

2

Answers


  1. The returned data property will be undefined until the query actually completes and has fetched data to provide. The UI code you have makes the assumption that data is defined.

    See the query hook’s signature, specifically the properties dealing with data and query status:

    // Derived request status booleans
    isUninitialized: boolean // Query has not started yet.
    isLoading: boolean // Query is currently loading for the first time. No data yet.
    isFetching: boolean // Query is currently fetching, but might have data from an earlier request.
    isSuccess: boolean // Query has data from a successful load.
    isError: boolean // Query is currently in an "error" state.
    

    You will want to at least check that data is defined prior to attempting to access into it for nested properties like data.image. The isSuccess is also a flag that indicates the query has data.

    Example:

    const ProductScreen = () => {
      const { id: productId } = useParams();
      const {
        data: product,
        isLoading,
        error
      } = useGetProductDetailsQuery(productId);
    
      if (!product) {
        return null; // or some fallback UI message about no data, etc
      }
    
      return (
        <>
          <Link className="btn btn-light my-3" to="/">Go Back</Link>
          <Row>
            <Col md={5}>
              <Image
                src={product.image} // <-- now safe to access
                alt={product.name}
                fluid
              />
            </Col>
            ...
          </Row>
        </>
      );
    };
    
    export default ProductScreen;
    
    Login or Signup to reply.
  2. @Drew Reese and @Basharat Khan, Please am still stucked here… Trying to understand what you guys meant about this " if (!product) {
    return null; // or some fallback UI message about no data, etc
    }"

    please explain better for me

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search