我是 JavaScript 的新手,我一直在学习创建电子商务网站的教程。出于某种原因,我现在收到一个 TypeError: Cannot read property 'category' of undefined error in reference:
export const listProducts = ({category = '' }) => async (dispatch)
我的 productAction.js 文档的一部分。我在下面包含了我的 productAction.js 文档的其余部分,以及其他重要的文档。我非常感谢您对此问题的任何帮助或指导。
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;
}
};
后端
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;
更新:
所以,我认为问题在于我的 listProducts 常量。在我的 HomeScreen.js 文档(以前没有包含)中,我意识到我忘记在 dispatch(listProducts()); 中添加 {};
useEffect(() =>{
dispatch(listProducts({}));
}, [dispatch]);
我唯一的问题是当我的页面现在加载时,我现在有一个请求失败,状态代码为 500 错误。
我已经包含了 HomeScreen.js 文档的其余部分以及包含 listProducts 的其他文档。任何额外的指导将不胜感激。
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>
);
}
答案 0 :(得分:0)
根据错误,您似乎在调用 listProducts
而没有传递任何参数。如果这是预期的,那么您可以为要解构 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 });
}
};
答案 1 :(得分:0)
我认为最合理的答案是(基于提供的错误),
错误是因为您没有传递任何东西。因此,javascript 试图不传播任何内容并给您错误“无法读取未定义的属性‘类别’”。
我知道这有点令人困惑,让我用一个例子来解释
假设你有一个函数
const test = ({context = ""}) => {
console.log(context)
}
如果你调用 test
函数而不传递像下面这样的对象
test();
这将产生错误“无法读取未定义的属性‘类别’”,因为函数定义试图传播一个对象并从该对象中获取上下文属性,但您什么也没传递。
相反,如果你调用函数
test({});
或
test({context=1});
这不会导致任何错误,因为函数定义会传播一个对象。
有关对象传播语法的更多详细信息,您可以参考MDN docs here
希望对您有所帮助。