I'm pretty new to javascript, and I've been following a tutorial to create an e-commerce website. For some reason I am now getting a TypeError: Cannot read property 'category' of undefined error in reference to:
export const listProducts = ({category = '' }) => async (dispatch)
part of my productAction.js document. I've included the rest of my productAction.js document below, as well as the other important documents. I would really appreciate any help or guidance on this issue.
productAction.js
import Axios from 'axios';
import {
PRODUCT_CREATE_FAIL,
PRODUCT_CREATE_REQUEST,
PRODUCT_CREATE_SUCCESS,
PRODUCT_DETAILS_FAIL,
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_UPDATE_REQUEST,
PRODUCT_UPDATE_SUCCESS,
PRODUCT_UPDATE_FAIL,
PRODUCT_DELETE_REQUEST,
PRODUCT_DELETE_FAIL,
PRODUCT_DELETE_SUCCESS,
PRODUCT_REVIEW_CREATE_REQUEST,
PRODUCT_REVIEW_CREATE_SUCCESS,
PRODUCT_REVIEW_CREATE_FAIL,
PRODUCT_CATEGORY_LIST_SUCCESS,
PRODUCT_CATEGORY_LIST_REQUEST,
PRODUCT_CATEGORY_LIST_FAIL,
/*PRODUCT_SAVE_REQUEST,*/
} from '../constants/productConstants';
export const listProducts = ({name = '', category = '' }) => async (dispatch) => {
dispatch({
type: PRODUCT_LIST_REQUEST,
});
try {
const { data } = await Axios.get(`/api/products?name=${name}category=${category}`);
dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: PRODUCT_LIST_FAIL, payload: error.message });
}
};
export const listProductCategories = () => async (dispatch) => {
dispatch({
type: PRODUCT_CATEGORY_LIST_REQUEST,
});
try {
const { data } = await Axios.get(`/api/products/categories`);
dispatch({ type: PRODUCT_CATEGORY_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: PRODUCT_CATEGORY_LIST_FAIL, payload: error.message });
}
};
export const detailsProduct = (productId) => async (dispatch) => {
dispatch({ type: PRODUCT_DETAILS_REQUEST, payload: productId });
try {
const { data } = await Axios.get(`/api/products/${productId}`);
dispatch({ type: PRODUCT_DETAILS_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: PRODUCT_DETAILS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const createProduct = () => async (dispatch, getState) => {
dispatch({ type: PRODUCT_CREATE_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.post(
'/api/products',
{},
{
headers: { Authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({
type: PRODUCT_CREATE_SUCCESS,
payload: data.product,
});
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: PRODUCT_CREATE_FAIL, payload: message });
}
};
export const updateProduct = (product) => async (dispatch, getState) => {
dispatch({ type: PRODUCT_UPDATE_REQUEST, payload: product });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.put(`/api/products/${product._id}`, product, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: PRODUCT_UPDATE_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: PRODUCT_UPDATE_FAIL, error: message });
}
};
export const deleteProduct = (productId) => async (dispatch, getState) => {
dispatch({ type: PRODUCT_DELETE_REQUEST, payload: productId });
const {
userSignin: { userInfo },
} = getState();
try {
const {data} = Axios.delete(`/api/products/${productId}`, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: PRODUCT_DELETE_SUCCESS });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: PRODUCT_DELETE_FAIL, payload: message });
}
};
export const createReview = (productId, review) => async (
dispatch,
getState
) => {
dispatch({ type: PRODUCT_REVIEW_CREATE_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.post(
`/api/products/${productId}/reviews`,
review,
{
headers: { Authorization: `Bearer ${userInfo.token}` },
}
);
dispatch({
type: PRODUCT_REVIEW_CREATE_SUCCESS,
payload: data.review,
});
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: PRODUCT_REVIEW_CREATE_FAIL, payload: message });
}
};
productReducer.js
const {
PRODUCT_LIST_REQUEST,
PRODUCT_LIST_SUCCESS,
PRODUCT_LIST_FAIL,
PRODUCT_CATEGORY_LIST_REQUEST,
PRODUCT_CATEGORY_LIST_SUCCESS,
PRODUCT_CATEGORY_LIST_FAIL,
} = require('../constants/productConstants');
export const productListReducer = (
state = { loading: true, products: [] },
action
) => {
switch (action.type) {
case PRODUCT_LIST_REQUEST:
return { loading: true };
case PRODUCT_LIST_SUCCESS:
return { loading: false, products: action.payload };
case PRODUCT_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
export const productCategoryListReducer = (
state = { loading: true, products: [] },
action
) => {
switch (action.type) {
case PRODUCT_CATEGORY_LIST_REQUEST:
return { loading: true };
case PRODUCT_CATEGORY_LIST_SUCCESS:
return { loading: false, categories: action.payload };
case PRODUCT_CATEGORY_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
Backend
productModel.js
import mongoose from 'mongoose';
const reviewSchema = new mongoose.Schema(
{
name: { type: String, required: true },
comment: { type: String, required: true },
rating: { type: Number, required: true },
},
{
timestamps: true,
}
);
const productSchema = new mongoose.Schema(
{
name: { type: String, required: true, unique: true },
image: { type: String, required: true },
brand: { type: String, required: true },
category: { type: String, required: true },
description: { type: String, required: true },
price: { type: Number, required: true },
countInStock: { type: Number, required: true },
rating: { type: Number, required: true },
numReviews: { type: Number, required: true },
reviews: [reviewSchema],
},
{
timestamps: true,
}
);
const Product = mongoose.model('Product', productSchema);
export default Product;
productRouter.js
import express from 'express';
import expressAsyncHandler from 'express-async-handler';
import data from '../data.js';
import Product from '../models/productModel.js';
import { isAdmin, isAuth } from '../utils.js';
const productRouter = express.Router();
productRouter.get(
'/',
expressAsyncHandler(async (req, res) => {
const name = req.query.name || '';
const category = req.query.category || '';
const nameFilter = name ? { name: { $regex: name, $options: 'i' } } : {};
const categoryFilter = category ? { category } : {};
const products = await Product.find({
...nameFilter,
...categoryFilter,
}).res.send(products);
})
);
productRouter.get(
'/categories',
expressAsyncHandler(async (req, res) => {
const categories = await Product.find().distinct('category');
res.send(categories);
})
);
productRouter.get(
'/seed',
expressAsyncHandler(async (req, res) => {
// await Product.remove({});
const createdProducts = await Product.insertMany(data.products);
res.send({ createdProducts });
})
);
productRouter.get(
'/:id',
expressAsyncHandler(async (req, res) => {
const product = await Product.findById(req.params.id);
if (product) {
res.send(product);
} else {
res.status(404).send({ message: 'Product Not Found' });
}
})
);
productRouter.post(
'/',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const product = new Product({
name: 'sample name ' + Date.now(),
image: '/images/p1.jpg',
price: 0,
category: 1,
brand: 'sample brand',
countInStock: 0,
rating: 0,
numReviews: 0,
description: 'sample description',
});
const createdProduct = await product.save();
res.send({ message: 'Product Created', product: createdProduct });
})
);
productRouter.put(
'/:id',
isAuth,
isAdmin,
expressAsyncHandler(async (req, res) => {
const productId = req.params.id;
const product = await Product.findById(productId);
if (product) {
product.name = req.body.name;
product.price = req.body.price;
product.image = req.body.image;
product.category = req.body.category;
product.brand = req.body.brand;
product.countInStock = req.body.countInStock;
product.description = req.body.description;
const updatedProduct = await product.save();
res.send({ message: 'Product Updated', product: updatedProduct });
} else {
res.status(404).send({ message: 'Product Not Found' });
}
})
);
export default productRouter;
Update:
So, I think the issue was my listProducts constant. In my HomeScreen.js document (not previously included), I realized that I had forgot to add {} within dispatch(listProducts());
useEffect(() =>{
dispatch(listProducts({}));
}, [dispatch]);
My only issue is that while my page now loads, I now have a Request failed with status code 500 error.
I've included the rest of my HomeScreen.js document and my other documents that include listProducts. Any additional guidance would be greatly appreciated.
HomeScreen.js
import React, { useEffect } from 'react';
import Product from '../components/Product';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import { useDispatch, useSelector } from 'react-redux';
import { listProducts } from '../actions/productActions';
import { Link } from 'react-router-dom';
export default function HomeScreen() {
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { loading, error, products } = productList;
useEffect(() =>{
dispatch(listProducts({}));
}, [dispatch]);
return (
<div>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<>
{products.length === 0 && <MessageBox>No Product Found</MessageBox>}
<div className="row center">
{products.map((product) => (
<Product key={product._id} product={product}></Product>
))}
</div>
</>
)}
</div>
);
}
SearchScreen.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useParams } from 'react-router-dom';
import { listProducts } from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import Product from '../components/Product';
export default function SearchScreen(props) {
const { name = 'all', category = 'all' } = useParams();
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { loading, error, products } = productList;
const productCategoryList = useSelector((state) => state.productCategoryList);
const {
loading: loadingCategories,
error: errorCategories,
categories,
} = productCategoryList;
useEffect(() => {
dispatch(
listProducts({
name: name !== 'all' ? name : '',
category: category !== 'all' ? category : '',
})
);
}, [category, dispatch, name]);
const getFilterUrl = (filter) => {
const filterCategory = filter.category || category;
const filterName = filter.name || name;
return `/search/category/${filterCategory}/name/${filterName}`;
};
return (
<div>
<div className="row">
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<div>{products.length} Results</div>
)}
</div>
<div className="row top">
<div className="col-1">
<h3>Department</h3>
{loadingCategories ? (
<LoadingBox></LoadingBox>
) : errorCategories ? (
<MessageBox variant="danger">{errorCategories}</MessageBox>
) : (
<ul>
{categories.map((c) => (
<li key={c}>
<Link
className={c === category ? 'active' : ''}
to={getFilterUrl({ category: c })}
>
{c}
</Link>
</li>
))}
</ul>
)}
</div>
<div className="col-3">
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<>
{products.length === 0 && (
<MessageBox>No Product Found</MessageBox>
)}
<div className="row center">
{products.map((product) => (
<Product key={product._id} product={product}></Product>
))}
</div>
</>
)}
</div>
</div>
</div>
);
}
ProductListScreen.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {
createProduct,
deleteProduct,
listProducts,
} from '../actions/productActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {
PRODUCT_CREATE_RESET,
PRODUCT_DELETE_RESET,
} from '../constants/productConstants';
export default function ProductListScreen(props) {
const productList = useSelector((state) => state.productList);
const { loading, error, products } = productList;
const productCreate = useSelector((state) => state.productCreate);
const {
loading: loadingCreate,
error: errorCreate,
success: successCreate,
product: createdProduct,
} = productCreate;
const productDelete = useSelector((state) => state.productDelete);
const {
loading: loadingDelete,
error: errorDelete,
success: successDelete,
} = productDelete;
const dispatch = useDispatch();
useEffect(() => {
if (successCreate) {
dispatch({ type: PRODUCT_CREATE_RESET });
props.history.push(`/product/${createdProduct._id}/edit`);
}
if (successDelete) {
dispatch({ type: PRODUCT_DELETE_RESET });
}
dispatch(listProducts());
/// TODO: dispatch delete action
}, [createdProduct, dispatch, props.history, successCreate, successDelete]);
const deleteHandler = (product) => {
if (window.confirm('Are you sure to delete?')) {
dispatch(deleteProduct(product._id));
}
};
const createHandler = () => {
dispatch(createProduct());
};
return (
<div>
<div className="row">
<h1>Products</h1>
<button type="button" className="primary" onClick={createHandler}>
Create Product
</button>
</div>
{loadingDelete && <LoadingBox></LoadingBox>}
{errorDelete && <MessageBox variant="danger">{errorDelete}</MessageBox>}
{loadingCreate && <LoadingBox></LoadingBox>}
{errorCreate && <MessageBox variant="danger">{errorCreate}</MessageBox>}
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<table className="table">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>PRICE</th>
<th>CATEGORY</th>
<th>BRAND</th>
<th>ACTIONS</th>
</tr>
</thead>
<tbody>
{products.map((product) => (
<tr key={product._id}>
<td>{product._id}</td>
<td>{product.name}</td>
<td>{product.price}</td>
<td>{product.category}</td>
<td>{product.brand}</td>
<td>
<button
type="button"
className="small"
onClick={() =>
props.history.push(`/product/${product._id}/edit`)
}
>
Edit
</button>
<button
type="button"
className="small"
onClick={() => deleteHandler(product)}
>
Delete
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
Based on the error it seems you may be calling listProducts and not passing any argument. If this is expected then you can provide an initial value for the argument to destructure category from.
export const listProducts = ({ category = '' } = {}) => async (dispatch) => {
dispatch({
type: PRODUCT_LIST_REQUEST,
});
try {
const { data } = await Axios.get(`/api/products?category=${category}`);
dispatch({ type: PRODUCT_LIST_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: PRODUCT_LIST_FAIL, payload: error.message });
}
};
SO the most plausible answer according to me is (based on the error provided),
The error is causing because you are not passing anything. Due to which the javascript is trying to spread nothing and giving you the error "Cannot read property 'category' of undefined".
I know this is a bit confusing let me explain it with an example
let's say you have a function
const test = ({context = ""}) => {
console.log(context)
}
and if you call the test function without passing an object like below
test();
this will produce the error "Cannot read property 'category' of undefined" as the function definition is trying to spread an object and get a context property out of that object but you are passing nothing.
instead, if you call the function
test({});
or
test({context=1});
this will not cause you any error as function definition will get an object to spread.
for more details on object spread syntax, you can refer MDN docs here
Hope its helpful.
Related
In this page the user can login, but if the untilDate is bigger than the current date it should log out the user. The code runs fine 1/2 times, the other giving me the error on the title.
I am working with createContext for user login. This is the AuthContext file
import React from "react";
import { createContext, useEffect, useReducer } from "react";
const INITIAL_STATE = {
user: JSON.parse(localStorage.getItem("user")) || null,
loading: false,
error: null,
};
export const AuthContext = createContext(INITIAL_STATE);
const AuthReducer = (state, action) => {
switch (action.type) {
case "LOGIN_START":
return {
user: null,
loading: true,
error: null,
};
case "LOGIN_SUCCESS":
return {
user: action.payload,
loading: false,
error: null,
};
case "LOGOUT":
return {
user: null,
loading: false,
error: null,
};
case "LOGIN_FAILURE":
return {
user: null,
loading: false,
error: action.payload,
};
case "UPDATE_USER_DATE":
const updatedUser = { ...state.user };
updatedUser.activeUntil = action.payload;
return {
...state,
user: updatedUser,
};
default:
return state;
}
};
export const AuthContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(AuthReducer, INITIAL_STATE);
useEffect(() => {
localStorage.setItem("user", JSON.stringify(state.user));
}, [state.user]);
return (
<AuthContext.Provider
value={{
user: state.user,
loading: state.loading,
error: state.error,
dispatch,
}}
>
{children}
</AuthContext.Provider>
);
};
When the user clicks the login button, it runs the handleClick function:
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
let date = new Date().toJSON();
let userdate = date;
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
userdate = user.activeUntil;
//do if date is <=current datem dispatch logout
} catch (err) {
if (userdate > date) {
console.log("undefined data");
} else {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
}
if (userdate > date) {
dispatch({ type: "LOGOUT" });
console.log("If you are seeing this your contract has expired");
} else {
// navigate("/myinfo");
}
};
The console error happens from this line dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
Is there a way I can bypass this error or a different way I can write my code to make it work?
This is the full code of login page
import React from "react";
import axios from "axios";
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import { AuthContext } from "../../context/AuthContext";
import {
Container,
FormWrap,
FormContent,
Form,
FormInput,
FormButton,
Icon,
FormH1,
SpanText,
IconWrapper,
IconL,
} from "./signinElements";
import Image from "../../images/Cover.png";
const Login = () => {
const [credentials, setCredentials] = useState({
namekey: undefined,
password: undefined,
});
/* */
// to view current user in console
const { user, loading, error, dispatch } = useContext(AuthContext);
let msg;
const navigate = useNavigate();
const handleChange = (e) => {
setCredentials((prev) => ({ ...prev, [e.target.id]: e.target.value }));
};
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
let date = new Date().toJSON();
let userdate = date;
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
userdate = user.activeUntil;
//do if date is <=current datem dispatch logout
} catch (err) {
if (userdate > date) {
console.log("undefined data");
} else {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
}
if (userdate > date) {
dispatch({ type: "LOGOUT" });
console.log("If you are seeing this your contract has expired");
} else {
// navigate("/myinfo");
}
};
// console.log(user.activeUntil); //type to view current user in console
return (
<>
<Container>
<IconWrapper>
<IconL to="/">
<Icon src={Image}></Icon>
</IconL>
</IconWrapper>
<FormWrap>
<FormContent>
<Form action="#">
<FormH1>
Sign in with the namekey and password written to you on your
contract.
</FormH1>
<FormInput
type="namekey"
placeholder="Namekey"
id="namekey"
onChange={handleChange}
required
/>
<FormInput
type="password"
placeholder="Password"
id="password"
onChange={handleChange}
/>
<FormButton disabled={loading} onClick={handleClick}>
Login
</FormButton>
<SpanText>{msg}</SpanText>
{error && <SpanText>{error.message}</SpanText>}
{error && (
<SpanText>
Forgot namekey or password? Contact our support team +355 69
321 5237
</SpanText>
)}
</Form>
</FormContent>
</FormWrap>
</Container>
</>
);
};
export default Login;
The problem was i was trying to call a localy stored user and 1 time it wasnt loaded and the other it was. Simply fixed it by changing the if statement to check directly in result details without having to look in local storage.
const [expired, setExpired] = useState(false);
const handleClick = async (e) => {
e.preventDefault();
dispatch({ type: "LOGIN_START" });
try {
const res = await axios.post("/auth/signin", credentials);
dispatch({ type: "LOGIN_SUCCESS", payload: res.data.details });
let date = new Date().toJSON();
if (res.data.details.activeUntil < date) {
dispatch({ type: "LOGOUT" });
console.log("Users contract has expired");
setExpired(!expired);
} else {
navigate("/myinfo");
}
} catch (err) {
dispatch({ type: "LOGIN_FAILURE", payload: err.response.data });
}
};
I'm pretty new to javascript and react, and I'm trying to create a page to return orders for an e-commerce website that I'm creating. I am trying to create this page in a similar way to which the orders are created. Also, I am trying to be able to create a return by pressing a make return button on a customers order history page. My issue is that the order returns are not being created, and currently, in my console in my order history page I'm getting a Uncaught TypeError: Cannot read properties of undefined (reading 'orderItems') error within the const placeDevolucionHandler = () => {dispatch(createDevolucion({ ...order, devolucionItems: order.orderItems })); }; line of my code. I'm unsure why I am having this problem as I followed all the steps that I had done when creating my orders from my cart. I would really appreciate any help or guidance on how to fix this issue.
Thank you!
Note: devolucion is the name that I used in place of return.
OrderHistoryScreen.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { listOrderMine, detailsOrder } from '../actions/orderActions';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
import {createDevolucion } from '../actions/devolucionActions';
import { DEVOLUCION_CREATE_RESET } from '../constants/devolucionConstants';
export default function OrderHistoryScreen(props) {
const orderId = props.match.params.id;
const order = useSelector((state) => state.order);
const devolucionCreate = useSelector((state) => state.devolucionCreate);
const {success, devolucion } = devolucionCreate;
const orderMineList = useSelector((state) => state.orderMineList);
const { loading, error, orders } = orderMineList;
const dispatch = useDispatch();
useEffect(() => {
dispatch(listOrderMine());
dispatch(detailsOrder(orderId));
}, [dispatch, orderId]);
const placeDevolucionHandler = () => {
dispatch(createDevolucion({ ...order, devolucionItems: order.orderItems }));
};
useEffect(() => {
if (success) {
props.history.push(`/devolucion/${devolucion._id}`);
dispatch({ type: DEVOLUCION_CREATE_RESET });
}
}, [dispatch, devolucion, props.history, success]);
return (
<div>
<h1>Order History</h1>
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
<table className="table">
<thead>
<tr>
<th>ACTIONS</th>
</tr>
</thead>
<tbody>
{orders.map((order) => (
<tr key={order._id}>
<td>
<button
type="button"
onClick={placeDevolucionHandler}
className="small"
>
Make a Return
</button>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
);
}
PlaceOrderScreen.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link } from 'react-router-dom';
import {createOrder, } from '../actions/orderActions';
import CheckoutSteps from '../components/CheckoutSteps';
import { ORDER_CREATE_RESET } from '../constants/orderConstants';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';
export default function PlaceOrderScreen(props) {
const cart = useSelector((state) => state.cart);
if (!cart.paymentMethod) {
props.history.push('/payment');
}
const orderCreate = useSelector((state) => state.orderCreate);
const { loading, success, error, order } = orderCreate;
const dispatch = useDispatch();
const placeOrderHandler = () => {
dispatch(createOrder({ ...cart, orderItems: cart.cartItems }));
};
useEffect(() => {
if (success) {
props.history.push(`/order/${order._id}`);
dispatch({ type: ORDER_CREATE_RESET });
}
}, [dispatch, order, props.history, success]);
return (
<div>
<div className="row top">
<div className="col-2">
<div className="card card-body">
<ul>
<li>
<h2>Order Summary</h2>
</li>
<li>
<button
type="button"
onClick={placeOrderHandler}
className="primary block"
disabled={cart.cartItems.length === 0}
>
Place Order
</button>
</li>
{loading && <LoadingBox></LoadingBox>}
{error && <MessageBox variant="danger">{error}</MessageBox>}
</ul>
</div>
</div>
</div>
</div>
);
}
devolucionActions.js
import Axios from 'axios';
import {
DEVOLUCION_CREATE_FAIL,
DEVOLUCION_CREATE_REQUEST,
DEVOLUCION_CREATE_SUCCESS,
DEVOLUCION_DETAILS_FAIL,
DEVOLUCION_DETAILS_REQUEST,
DEVOLUCION_DETAILS_SUCCESS,
DEVOLUCION_PAY_REQUEST,
DEVOLUCION_PAY_FAIL,
DEVOLUCION_PAY_SUCCESS,
DEVOLUCION_MINE_LIST_REQUEST,
DEVOLUCION_MINE_LIST_FAIL,
DEVOLUCION_MINE_LIST_SUCCESS,
DEVOLUCION_LIST_REQUEST,
DEVOLUCION_LIST_SUCCESS,
DEVOLUCION_LIST_FAIL,
DEVOLUCION_DELETE_REQUEST,
DEVOLUCION_DELETE_SUCCESS,
DEVOLUCION_DELETE_FAIL,
DEVOLUCION_SHIPPED_REQUEST,
DEVOLUCION_SHIPPED_SUCCESS,
DEVOLUCION_SHIPPED_FAIL,
DEVOLUCION_DELIVER_REQUEST,
DEVOLUCION_DELIVER_SUCCESS,
DEVOLUCION_DELIVER_FAIL,
DEVOLUCION_SUMMARY_REQUEST,
DEVOLUCION_SUMMARY_SUCCESS,
} from '../constants/devolucionConstants';
export const createDevolucion = (devolucion) => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_CREATE_REQUEST, payload: devolucion });
try {
const {
userSignin: { userInfo },
} = getState();
const { data } = await Axios.post('/api/devolucions', devolucion, {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
});
dispatch({ type: DEVOLUCION_CREATE_SUCCESS, payload: data.devolucion });
} catch (error) {
dispatch({
type: DEVOLUCION_CREATE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
export const detailsDevolucion = (devolucionId) => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_DETAILS_REQUEST, payload: devolucionId });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get(`/api/devolucions/${devolucionId}`, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: DEVOLUCION_DETAILS_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: DEVOLUCION_DETAILS_FAIL, payload: message });
}
};
export const listDevolucion = ({ seller = '' }) => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_LIST_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get(`/api/devolucions?seller=${seller}`, {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
console.log(data);
dispatch({ type: DEVOLUCION_LIST_SUCCESS, payload: data });
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({ type: DEVOLUCION_LIST_FAIL, payload: message });
}
};
export const summaryDevolucion = () => async (dispatch, getState) => {
dispatch({ type: DEVOLUCION_SUMMARY_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get('/api/devolucions/summary', {
headers: { Authorization: `Bearer ${userInfo.token}` },
});
dispatch({ type: DEVOLUCION_SUMMARY_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: DEVOLUCION_CREATE_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
store.js
import { createStore, compose, applyMiddleware, combineReducers } from 'redux';
import thunk from 'redux-thunk';
import { cartReducer } from './reducers/cartReducers';
import {
devolucionCreateReducer,
devolucionDeleteReducer,
devolucionShipReducer,
devolucionDeliverReducer,
devolucionDetailsReducer,
devolucionListReducer,
devolucionMineListReducer,
devolucionPayReducer,
devolucionSummaryReducer,
} from './reducers/devolucionReducers';
import {
orderCreateReducer,
orderDeleteReducer,
orderShipReducer,
orderDeliverReducer,
orderDetailsReducer,
orderListReducer,
orderMineListReducer,
orderPayReducer,
orderSummaryReducer,
} from './reducers/orderReducers';
import {
productCategoryListReducer,
productCreateReducer,
productDeleteReducer,
productDetailsReducer,
productListReducer,
productReviewCreateReducer,
productUpdateReducer,
} from './reducers/productReducers';
import {
userDeleteReducer,
userDetailsReducer,
userListReducer,
userRegisterReducer,
userSigninReducer,
userUpdateProfileReducer,
userUpdateReducer,
} from './reducers/userReducers';
const initialState = {
userSignin: {
userInfo: localStorage.getItem('userInfo')
? JSON.parse(localStorage.getItem('userInfo'))
: null,
},
cart: {
cartItems: localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [],
shippingAddress: localStorage.getItem('shippingAddress')
? JSON.parse(localStorage.getItem('shippingAddress'))
: {},
paymentMethod: 'PayPal',
},
};
const reducer = combineReducers({
productList: productListReducer,
productDetails: productDetailsReducer,
productCreate: productCreateReducer,
productUpdate: productUpdateReducer,
productDelete: productDeleteReducer,
productReviewCreate: productReviewCreateReducer,
productCategoryList: productCategoryListReducer,
cart: cartReducer,
userSignin: userSigninReducer,
userRegister: userRegisterReducer,
userList: userListReducer,
userDelete: userDeleteReducer,
userDetails: userDetailsReducer,
userUpdateProfile: userUpdateProfileReducer,
userUpdate: userUpdateReducer,
orderCreate: orderCreateReducer,
orderDetails: orderDetailsReducer,
orderPay: orderPayReducer,
orderMineList: orderMineListReducer,
orderList: orderListReducer,
orderDelete: orderDeleteReducer,
orderShip: orderShipReducer,
orderDeliver: orderDeliverReducer,
devolucionCreate: devolucionCreateReducer,
devolucionDetails: devolucionDetailsReducer,
devolucionPay: devolucionPayReducer,
devolucionMineList: orderMineListReducer,
devolucionSummary: devolucionSummaryReducer,
devolucionList: devolucionListReducer,
devolucionDelete: devolucionDeleteReducer,
devolucionShip: devolucionShipReducer,
devolucionDeliver: devolucionDeliverReducer,
devolucionSummary: devolucionSummaryReducer,
});
const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(
reducer,
initialState,
composeEnhancer(applyMiddleware(thunk))
);
export default store;
I'm getting troubles about showing product, when I try to GET api/products/:id, my browser return localhost:3000/produit/undefined, and Request failed with status code 500
link repo : https://github.com/Kwonsongji/mern-stack-calone-shop
Here is my part Back :
Model : Product.js
const mongoose = require('mongoose');
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
image: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
material: {
type: String,
required: true
},
countInStock: {
type: Number,
required: true
},
}, {
timestamps: true
})
const Product = mongoose.model('Product', productSchema);
module.exports = Product;
Controller : productCtrl.js
const db = require('../config/db');
const Product = require('../models/Product');
const datas = require('../data/datas');
const productCtrl = {
sendDatasProductToMoongoose: async (req, res) => {
try {
//await db.products.dropIndexes({})
await Product.deleteMany({})
//.remove({});
const createdProducts = await Product.insertMany(datas.products);
console.log('Data Import Sucess createdProducts',createdProducts);
res.send({ createdProducts});
} catch (error) {
res.status(500).send(error.message)
}
},
getAllProducts: async (req, res) => {
try {
console.log("req :", req.body);
// Product.find({})
const products = await Product.find()
.then((Document) => {
console.log("Document: ", Document);
console.log("Req.params: ", req.params);
if (!Document) {
return res
.status(404)
.json({ message: "This ressource doesn't exist " });
}
res.status(200).json(datas.products);
})
.catch((error) => {
console.log(error);
res.status(500).json(error)
});
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server Error" });
}
},
getProductById: async (req, res) => {
try {
/* const product = await Product.findOne(_id) */
const product = await Product.findById(req.params.id)
/* .then((Document) => {
if (!Document) {
return res
.status(404)
.json({ message: "This document doesn't exist" });
}
res.status(200).json(Document);
})
.catch((error) => {
res.status(500).json(error);
}); */
res.send(product); // returne moi les données en json
} catch (error) {
console.error(error);
res.status(500).json({ message: "Server Error" });
}
},
};
module.exports = productCtrl;
Router : product.js
const productCtrl = require('../controllers/productCtrl');
const router = express.Router();
router.get('/seed', productCtrl.sendDatasProductToMoongoose )
router.get('/', productCtrl.getAllProducts);
router.get('/:id', productCtrl.getProductById);
module.exports = router;
Server : server.js
const express = require("express");
const cors = require("cors");
// require connexion db and run it
const connectDB = require("./config/db");
const app = express();
connectDB();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use("/api/products",require("./routes/product.js"));
app.use("/api/users",require("./routes/user.js"))
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server running on ${PORT}`));
Frontend :
productActions :
import * as actionTypes from '../constants/productConstants';
import axios from 'axios';
export const getProducts = () => async (dispatch) => {
try {
dispatch({ type: actionTypes.GET_PRODUCTS_REQUEST });
const { data } = await axios.get(`/api/products`);
dispatch({
type: actionTypes.GET_PRODUCTS_SUCESS,
payload: data
})
} catch (error) {
dispatch({
type: actionTypes.GET_PRODUCTS_FAIL,
payload: error.response && error.response.message
? error.response.data.message
: error.message
});
}
};
export const getProductDetails = (id) => async (dispatch) => {
try {
dispatch({ type: actionTypes.GET_PRODUCT_DETAILS_REQUEST });
const { data } = await axios.get(`/api/products/${id}`);
dispatch({
type: actionTypes.GET_PRODUCT_DETAILS_SUCESS,
payload: data
})
} catch (error) {
dispatch({
type: actionTypes.GET_PRODUCT_DETAILS_FAIL,
payload: error.response && error.response.message
? error.response.data.message
: error.message
});
}
};
export const removeProductDetails = () => (dispatch) => {
dispatch({ type: actionTypes.GET_PRODUCT_DETAILS_RESET });
};
productReducer.js:
import * as actionTypes from '../constants/productConstants';
const initialStateForGetProducts = {
products: []
};
export const getProductsReducer = (state = initialStateForGetProducts, action) => {
switch (action.type) {
case actionTypes.GET_PRODUCTS_REQUEST:
return {
loading: true,
products:[]
}
case actionTypes.GET_PRODUCTS_SUCESS:
return {
loading: false,
products: action.payload
}
case actionTypes.GET_PRODUCTS_FAIL:
return {
loading: false,
error: action.payload //on veut afficher l'erreur
}
default:
return state;
}
}
const initialStateForGetOneProduct = {
product: {}
}
export const getProductDetailsReducer = (state = initialStateForGetOneProduct, action) => {
switch (action.type) {
case actionTypes.GET_PRODUCT_DETAILS_REQUEST:
return {
loading: true,
}
case actionTypes.GET_PRODUCT_DETAILS_SUCESS:
return {
loading: false,
product: action.payload
}
case actionTypes.GET_PRODUCT_DETAILS_FAIL:
return {
loading: false,
error: action.payload
}
case actionTypes.GET_PRODUCT_DETAILS_RESET:
return {
product: {}
}
default:
return state;
}
}
ProductScreen.js
/* eslint-disable react/prop-types */
import React from 'react';
import './style.scss';
//import moonPendant from '../../assets/fonts/moon-pendant.png';
import { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
// Actions
import { getProductDetails } from '../../../redux/actions/productActions';
import { addToCart } from '../../../redux/actions/cartActions';
const ProductScreen = ( {match, history}) => {
const [qty, setQty] = useState(1);
const dispatch = useDispatch();
const productDetails = useSelector(state => state.getProductDetails);
const { loading, error, product } = productDetails;
useEffect(() => {
if (product && match.params.id !== product._id) {
dispatch(getProductDetails(match.params.id));
}
}, [dispatch, product, match]);
const addToCartHandler = () => {
dispatch(addToCart(product._id, qty));
history.push("/panier");
}
return (
<div className="productScreen">
{ loading ? <h2>Loading...</h2>
: error ? <h2>{error}</h2>
: (
<>
<div className="productScreen__left">
<div className="productScreen__left__image">
{/* <img src={moonPendant} alt={product.name} /> */}
<img src={product.image} alt={product.name} />
</div>
<div className="productScreen__left__info">
<p className="productScreen__left__name"> {product.name}</p>
<p className="productScreen__left__price"> {product.price}</p>
<p className="productScreen__left__material"> {product.material}</p>
</div>
</div>
<div className="productScreen__right">
<div className="productScreen__right__info">
<p className="productScreen__right__price">
Prix: <span> {product.price} euros </span>
</p>
<p className="productScreen__right__status">
Statut:
<span>
{product.countInStock > 0 ? "En Stock" : "En Rupture de Stock"}
</span>
</p>
<p className="productScreen__right__qty">
Quantité:
<select value={qty} /* lorsqu'on slct le nbre voulu */
onChange={(e) => setQty(e.target.value)}>
{[...Array(product.countInStock).keys()].map((x) => (
<option
key={x+1}
value={x+1}
>
{x+1}
</option> //on utilise l'instance array pour créer un tableau de liste
))}
</select>
</p>
<p>
<button
type="button"
onClick={ addToCartHandler}
className="productScreen__right__add"
> Ajouter
</button>
</p>
</div>
</div>
</>
)}
</div>
)
}
export default ProductScreen;
link repo : https://github.com/Kwonsongji/mern-stack-calone-shop
I'm trying to use formik for registration/login form and catch Formik.tsx:826 Warning: An unhandled error was caught from submitForm() TypeError: Cannot read property 'then' of undefined Please check my code. I lost my .then from here...
loginForm.js
import { withFormik } from "formik";
import LoginForm from "../components/loginForm";
import store from "../../../redux/store";
import { usersActions } from "../../../redux/actions";
const LoginFormContainer = withFormik({
enableReinitialize: true,
mapPropsToValues: () => ({
email: "",
password: "",
}),
validate: (values) => {
const errors = {};
if (!values.email) {
errors.email = "Обязательное поле";
} else if (
!/^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(values.email)
) {
errors.email = "Неверный email адрес";
}
if (!values.password) {
errors.password = "Введите пароль";
}
return errors;
},
handleSubmit: (values, { setSubmitting, props }) => {
store
.dispatch(usersActions.fetchUserLogin(values))
.then(({data}) => {
props.history.push('/')
})
.catch((e) => {console.log(e);});
},
displayName: "LoginForm",
})(LoginForm);
export default LoginFormContainer;
Here is my usersApi.js i'm using axios to post data on my back
import { axios } from '../../core';
export default {
signin: (postData) => axios.post('/user/signin', postData),
}
and this is my redux actions
userAction.js
import { usersApi } from "../../helpers/api";
import { notification } from "antd";
const actions = {
setUser: (data) => ({
type: "USERS:SET_DATA",
payload: data,
}),
setIsAuth: (bool) => ({
type: "USER:SET_IS_AUTH",
payload: bool,
}),
fetchUserLogin: (postData) => (dispatch) => {
usersApi
.signin(postData)
.then(({ data }) => {
console.log(data);
if (data.error) {
const openNotificationWithIcon = (type) => {
notification[type]({
message: data.error,
description: data.message,
});
};
openNotificationWithIcon("error");
} else {
const openNotificationWithIcon = (type) => {
notification[type]({
message: "Успех!",
description: "Добро пожаловать!",
});
};
openNotificationWithIcon("success");
window.axios.defaults.headers.common["token"] = data.token;
window.localStorage["token"] = data.token;
dispatch(actions.setUser(data));
dispatch(actions.setIsAuth(true));
}
return data;
})
.catch((e) => {
const openNotificationWithIcon = (type) => {
notification[type]({
message: "Что то пошло не так",
description: "Попробуйте ещё раз позже",
});
};
openNotificationWithIcon("error");
dispatch(actions.setIsAuth(false));
});
},
export default actions;
I'm looking for hours and can't find problem.
i am working on a project and creating a form to create tours. everything is working fine just the issue is form input values are exchanging
for ex -
actual output- { tourName: 'pune darshan', location: '999', price: 'pune' }
expected output :- { tourName: 'pune darshan', location: 'pune', price: '999' }
i dont know where i am going wrong i am stuck here since 6 hrs
here is what i have tried
form component
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { createTour } from "../../store/slices/tourSlice";
import "./createListing.scss";
const CreateListing = () => {
const [tour, setTour] = useState({
tourName: "",
price: "",
location: "",
});
const dispatch = useDispatch();
const handleInput = (event) => {
setTour((tour) => ({
...tour,
[event.target.name]: event.target.value,
}));
};
const handleSubmit = (event) => {
event.preventDefault();
dispatch(createTour(tour.tourName, tour.price, tour.location));
};
return (
<div>
<div className='form-controller'>
<form action='' method='post' onSubmit={handleSubmit}>
<div className='form-group'>
<input
type='text'
className='form-control'
name='tourName'
placeholder='Enter Tour Name'
onChange={handleInput}
required
/>
</div>
<div className='form-group'>
<input
type='text'
className='form-control'
name='location'
placeholder='Enter Tour Location'
onChange={handleInput}
required
/>
</div>
<div className='form-group'>
<input
type='number'
className='form-control'
name='price'
placeholder='Enter Tour Cost'
onChange={handleInput}
required
/>
</div>
<div className='text-center'>
<button type='submit theme-btn'>Create Tour</button>
</div>
</form>
</div>
</div>
);
};
export default CreateListing;
here is the redux toolkit file
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
import { history } from "./../../helpers/history";
export const initialState = {
tourName: "",
location: "",
price: "",
error: "",
loading: false,
};
const tourSlice = createSlice({
name: "tour",
initialState,
reducers: {
tourCreateRequest: (State, action) => {
return {
loading: true,
};
},
tourCreateSuccess: (state, action) => {
return { loading: false, tourInfo: action.payload };
},
tourCreateFail: (state, action) => {
return {
loading: false,
error: action.payload,
};
},
},
});
const {
tourCreateFail,
tourCreateRequest,
tourCreateSuccess,
} = tourSlice.actions;
export default tourSlice.reducer;
export const createTour = (tourName, location, price) => async (dispatch) => {
try {
dispatch(tourCreateRequest);
const tourData = {
tourName,
location,
price,
};
const res = await axios.post(
"http://localhost:3000/api/v1/tours",
tourData
);
if (res) {
dispatch(tourCreateSuccess);
// history.push("/dashboard");
} else {
dispatch(
tourCreateFail(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
);
console.log("error");
}
} catch (error) {
dispatch(
tourCreateFail(
error.response && error.response.data.message
? error.response.data.message
: error.message
)
);
}
};
here is the model file
const mongoose = require("mongoose");
const tourSchema = mongoose.Schema(
{
tourName: { type: String },
rating: { type: String, default: 4.5 },
location: { type: String },
price: { type: String, default: 999 },
},
{ timestamps: {} }
);
const Tour = mongoose.model("Tour", tourSchema);
module.exports = Tour;
here is controller code
const createTours = async (req, res, next) => {
const { tourName, price, location } = req.body;
console.log(req.body);
try {
const newTour = new Tour({
tourName,
price,
location,
});
newTour.save();
res.status(200).json({
status: "success",
newTour,
});
} catch (error) {
res.status(404).json({
status: "failed",
error: error,
});
}
};
You pass the parameters in the createTour function in the wrong order.
You should update the dispatch line:
dispatch(createTour(tour.tourName, tour.location, tour.price));