#javascript #node.js #reactjs #mongodb #redux
Вопрос:
У меня возникают проблемы с отображением продукта, когда я пытаюсь ПОЛУЧИТЬ api/products/:id
, мой браузер возвращается localhost:3000/produit/undefined
, и запрос не удался с кодом состояния 500
репо ссылок : https://github.com/Kwonsongji/mern-stack-calone-shop Вот моя обратная часть : Модель : 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;
Контроллер : 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 amp;amp; 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 amp;amp; 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 amp;amp; 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;
репо ссылок : https://github.com/Kwonsongji/mern-stack-calone-shop
Комментарии:
1. Можете ли вы показать свой файл маршрутов в интерфейсе?
2.
const { data } = await axios.get(
/api/продукты/${id});
идентификатор может быть здесь не определен, проверьте перед отправкой запроса3. Если он там не определен, проверьте
dispatch(getProductDetails(match.params.id));
match.params.id не определено4. Нам нужно только увидеть, как вы формируете свои маршруты в пользовательском интерфейсе и как это
ProductScreen
позволяет получить доступ к параметрам маршрута. Остальное в значительной степени просто шум на данный момент. Пожалуйста, обновите вопрос, чтобы показать, какProductScreen
визуализируется и, возможно, получает реквизиты маршрута. Ваше репо на github также кажется очень пустым.5. @Prana @DrewRiese вот мой маршрут с фронтенда :
<Route exact path="/produit/:id" component={ProductScreen}></Route>