Hi I am building an Ecommerce Application using MERN Stack with redux. But having issues when i try to send price array arguement in my fetching function. It is giving me the error that XHR GET http://localhost:3000/api/v1/products?keyword=&page=1&price[gt]=undefined&price[lt]=undefined [HTTP/1.1 400 Bad Request 12ms]. Here is my Products.jsx Component Code {success:false,message:'Resource not found invalid Price'}
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Slider, Typography } from '@material-ui/core';
import { useAlert } from 'react-alert';
import { clearErrors, getProducts } from '../../actions/productActions';
import Loader from '../layout/Loader/Loader';
import ProductCard from '../Home/ProductCard';
import './Products.css';
import Pagination from 'react-js-pagination';
const Products = ({ match }) => {
const dispatch = useDispatch();
const alert = useAlert();
const [currentPage, setCurrentPage] = useState(1);
const [price, setPrice] = useState(0, 25000);
const { loading, products, productsCount, error, resultPerPage} =
useSelector((state) => state.products);
const keyword = match.params.keyword;
const setCurrentPageNo = (e) => {
setCurrentPage(e);
};
const priceHandler = (e, newPrice) => {
setPrice(newPrice);
};
useEffect(() => {
if (error) {
alert.error(error);
dispatch(clearErrors());
}
dispatch(getProducts(keyword, currentPage, price));
}, [dispatch, keyword, currentPage]);
return (
<>
{loading ? (
<Loader />
) : (
<>
<h2 className='productsHeading'>Products</h2>
<div className='products'>
{products &&
products.map((product) => (
<ProductCard key={product._id} product={product} />
))}
</div>
<div className='filterBox'>
<Typography>Price</Typography>
<Slider
value={price}
onChange={priceHandler}
valueLabelDisplay='auto'
aria-labelledby='range-slider'
min={0}
max={25000}
/>
</div>
{resultPerPage < productsCount && (
<div className='paginationBox'>
<Pagination
activePage={currentPage}
itemsCountPerPage={resultPerPage}
totalItemsCount={productsCount}
onChange={setCurrentPageNo}
nextPageText='Next'
prevPageText='Prev'
firstPageText='1st'
lastPageText='Last'
itemClass='page-item'
linkClass='page-link'
activeClass='pageItemActive'
activeLinkClass='pageLinkActive'
/>
</div>
)}
</>
)}
</>
);
};
export default Products;
And here is my action for getting allproducts
import axios from 'axios';
import {
ALL_PRODUCT_REQUEST,
ALL_PRODUCT_SUCCESS,
ALL_PRODUCT_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
CLEAR_ERRORS,
} from '../constants/productConstants';
export const getProducts =
(keyword = '', currentPage = 1, price = [0, 25000]) =>
async (dispatch) => {
try {
dispatch({
type: ALL_PRODUCT_REQUEST,
});
const { data } = await axios.get(
`/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gt]=${price[0]}&price[lt]=${price[1]}`
);
dispatch({
type: ALL_PRODUCT_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ALL_PRODUCT_FAIL,
payload: error.response.data.message,
});
}
};
Here is the Reducer for Products
export const productReducer = (state = { products: [] }, action) => {
switch (action.type) {
case ALL_PRODUCT_REQUEST:
return {
loading: true,
products: [],
};
case ALL_PRODUCT_SUCCESS:
return {
loading: false,
products: action.payload.products,
productsCount: action.payload.productsCount,
resultPerPage: action.payload.resultPerPage,
};
case ALL_PRODUCT_FAIL:
return {
loading: false,
error: action.payload,
};
case CLEAR_ERRORS:
return {
...state,
error: null,
};
default:
return state;
}
};
I am Using Custom Error Handling Class and here is the code for this
class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = ErrorResponse;
And here is the middleware function
const ErrorResponse = require('../utils/errorResponse');
const errorHandler = (err, req, res, next) => {
err.statusCode = err.statusCode || 500;
err.message = err.message || 'Internal Server Error';
// Wrong Mongodb Id error
if (err.name === 'CastError') {
const message = `Resource not found. Invalid: ${err.path}`;
err = new ErrorResponse(message, 400);
}
// Mongoose duplicate key error
if (err.code === 11000) {
const message = `Duplicate ${Object.keys(err.keyValue)} Entered`;
err = new ErrorResponse(message, 400);
}
// Wrong JWT error
if (err.name === 'JsonWebTokenError') {
const message = `Json Web Token is invalid, Try again `;
err = new ErrorResponse(message, 400);
}
// JWT EXPIRE error
if (err.name === 'TokenExpiredError') {
const message = `Json Web Token is Expired, Try again `;
err = new ErrorResponse(message, 400);
}
res.status(err.statusCode).json({
success: false,
message: err.message,
});
};
module.exports = errorHandler;
Here is my ApiFeatures class for filtering, Pagination and Sorting
class ApiFeatures {
constructor(query, queryStr) {
this.query = query;
this.queryStr = queryStr;
}
search() {
const keyword = this.queryStr.keyword
? {
name: {
$regex: this.queryStr.keyword,
$options: 'i',
},
}
: {};
this.query = this.query.find({ ...keyword });
return this;
}
filter() {
const queryCopy = { ...this.queryStr };
//Removing fields
const removFields = ['keyword', 'page', 'limit'];
removFields.forEach((key) => delete queryCopy[key]);
let queryStr = JSON.stringify(queryCopy);
queryStr = queryStr.replace(/\b(gt|gte|lt|lte)\b/g, (key) => `$${key}`);
this.query = this.query.find(JSON.parse(queryStr));
return this;
}
pagination(resultPerPage) {
const currentPage = Number(this.queryStr.page) || 1;
const skip = resultPerPage * (currentPage - 1);
this.query = this.query.limit(resultPerPage).skip(skip);
return this;
}
}
module.exports = ApiFeatures;
Here is my controller function
const getProducts = asyncHandler(async (req, res, next) => {
const resultPerPage = 8;
const productsCount = await Product.countDocuments();
const apiFeatures = new ApiFeatures(Product.find(), req.query)
.search()
.filter()
.pagination(resultPerPage);
const products = await apiFeatures.query;
res.status(200).json({
success: true,
count: products.length,
productsCount,
resultPerPage,
products,
});
});
My Product Model
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
name: {
type: String,
required: [true, 'Please Enter product Name'],
trim: true,
},
description: {
type: String,
required: [true, 'Please Enter product Description'],
},
price: {
type: Number,
required: [true, 'Please Enter product Price'],
maxLength: [8, 'Price cannot exceed 8 characters'],
},
ratings: {
type: Number,
default: 0,
},
images: [
{
public_id: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
},
],
category: {
type: String,
required: [true, 'Please Enter Product Category'],
},
Stock: {
type: Number,
required: [true, 'Please Enter product Stock'],
maxLength: [4, 'Stock cannot exceed 4 characters'],
default: 1,
},
numOfReviews: {
type: Number,
default: 0,
},
reviews: [
{
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
name: {
type: String,
required: true,
},
rating: {
type: Number,
required: true,
},
comment: {
type: String,
required: true,
},
},
],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
createdAt: {
type: Date,
default: Date.now,
},
});
module.exports = mongoose.model('Product', productSchema);
I am not figuring it out that what is the issue coz i have tested this api on postman and it is giving correct results. Help me to resolve this issue
CodePudding user response:
By a first look, the url on which you're sending the request is http://localhost:3000/api/v1/products?keyword=&page=1&price[gt]=undefined&price[lt]=undefined
.
I feel the problem is generated by sending undefined
values.
First, initialize price in your state to an array of 2 elements if you're accessing that way in the action.
const [price, setPrice] = useState([0, 25000]);
Second, make sure you are getting the right value from your filter
const priceHandler = (e, newPrice) => {
console.log(newPrice) // Maybe the value you need isn't being passed in the call
setPrice(newPrice);
};
Also, you can inspect the value of price coming in the action and see if it's really an array.
export const getProducts =
(keyword = '', currentPage = 1, price = [0, 25000]) =>
async (dispatch) => {
console.log(price) // and look at the value here
try {
dispatch({
type: ALL_PRODUCT_REQUEST,
});
const { data } = await axios.get(
`/api/v1/products?keyword=${keyword}&page=${currentPage}&price[gt]=${price[0]}&price[lt]=${price[1]}`
);
// Rest of the function ...
};