使用React-Router-Dom V6的Cartitems路由误差
我正在尝试将“ Cartitems”功能引入我的React-Redux应用程序,并将添加的数据存储在浏览器的本地存储中。
实际上,当我尝试通过单击“纳维托”部分的“购物车链接”来显示购物车时,问题就会引起。错误消息是'获取http:// localhost:3000/product/undefined 500(内部服务器错误)'和''und offrack(in Promise)'。知道如何解决这个问题。
注意:在两种情况下,同一组件“ cartscreen.js”都会在两种情况下显示购物车项目,当将新项目添加到CART&当还单击Navbar的购物车链接时。
请按照代码片段
感谢&在
app.js
import Header from './components/Header';
import { Container } from 'react-bootstrap';
import HomeScreen from './screens/HomeScreen';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ProductScreen from './screens/ProductScreen';
import CartScreen from './screens/CartScreen';
function App() {
return (
<Router>
<Header />
<main className="py-3">
<Container>
<Routes>
<Route path="/" element={<HomeScreen />} exact />
<Route path="/product/">
<Route path=":id" element={<ProductScreen />} />
<Route index element={<ProductScreen />} />
</Route>
<Route path="/cart" >
<Route index element={<CartScreen />} />
<Route path=":productid" element={<CartScreen />} />
</Route>
</Routes>
</Container>
</main>
<Footer />
</Router>
);
}
export default App;
productscreen.js
import { useParams, Link, useNavigate } from 'react-router-dom';
import {Row,Col,Image,ListGroup,Button,Card,Form} from 'react-bootstrap';
import Rating from '../components/Rating';
import { listProductDetails } from '../actions/productActions';
import { useDispatch, useSelector } from 'react-redux';
import Loader from '../components/Loader';
import Message from '../components/Message';
function ProductScreen() {
const { id } = useParams();
const navigate = useNavigate();
const [qty, setQty] = useState(1);
const dispatch = useDispatch();
const productListDetail = useSelector((state) => state.productDetail);
const { loading, error, product } = productListDetail;
useEffect(() => {
dispatch(listProductDetails(id));
}, [dispatch, id]);
const addToCartHandler = () => {
navigate(`/cart/${id}?qty=${qty}`);
};
return (
<div> <Link to={-1} className="btn btn-primary my-3">Go Back</Link>
{loading ? (<Loader />): error ? (<Message variant="danger">{error}</Message>) : (
<Row>
<Col md={6}>
<Image src={product.image} alt={product.name} fluid />
</Col>
<Col md={3}>
<ListGroup variant="flush">
<ListGroup.Item>
<h3> {product.name}</h3>
</ListGroup.Item>
<ListGroup.Item>
<Rating value={product.rating} text={`${product.numReviews} reviews`}
color={'#fae500'}/>
</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.Item>
<Row>
<Col> Status: </Col>
<Col>
<strong>
{product.countInStock > 0 ? 'In Stock' : 'Out of Stock'}
</strong>
</Col>
</Row>
</ListGroup.Item>
{product.countInStock > 0 && (
<ListGroup.Item>
<Row>
<Col> Qty </Col>
<Col xs="auto" className="my-1">
<Form.Control as="select" value={qty}
onChange={(e) => setQty(e.target.value)}>
{[...Array(product.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>{x + 1}</option>))}
</Form.Control>
</Col>
</Row>
</ListGroup.Item>)}
<ListGroup.Item>
<Button onClick={addToCartHandler}
className="btn btn-primary container-fluid"
disabled={product.countInStock === 0}
type="button">
Add to Cart
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
)}
</div>
);
}
export default ProductScreen;
cartscreen.js
import React, { useEffect } from 'react';
import { Col, ListGroup,Row,} from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate, useParams, Link, Outlet } from 'react-router-dom';
import { addToCart } from '../actions/cartAction';
import Message from '../components/Message';
const CartScreen = () => {
const { search } = useLocation();
const { productid } = useParams();
const qty = search ? Number(search.split('=')[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
useEffect(() => {
dispatch(addToCart(productid, qty));
},[dispatch, productid, qty]);
return (
<Row>
<Col md={8}> {cartItems.length === 0 ? (<Message variant="info">
Go Back To Home Page <Link to="/"></Link> </Message> ) : (
<ListGroup> {cartItems.map((x) => (
<ListGroup.Item key={x.product}>
{x.name} , {x.qty}
</ListGroup.Item> ))}
</ListGroup>)}
</Col>
<Col md={4}></Col>
</Row>
);
};
export default CartScreen;
cartreducers.js
import { CART_ADD_ITEM } from '../constants/cartConstants';
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find((x) => x.product === item.product);
if (existItem) {
return {
...state, cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x),};}
else {
return {
...state, cartItems: [...state.cartItems, item],};}
default:
return state;
}
};
cartaction.js
import axios from 'axios';
import { CART_ADD_ITEM } from '../constants/cartConstants';
export const addToCart = (productid, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/products/${productid}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
};
store.js
import { legacy_createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from '@redux-devtools/extension';
import {
productDetailsReducer,
productListReducer,
} from './reducers/productReducers';
import { cartReducer } from './reducers/cartReducers';
const reducer = combineReducers({
productList: productListReducer,
productDetail: productDetailsReducer,
cart: cartReducer,
});
const cartItemsFromStorage = localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [];
const initialState = { cart: { cartItems: cartItemsFromStorage } };
const middleware = [thunk];
const store = legacy_createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
I am trying to introduce 'cartItems' functionality to my react-redux app and store the added data in the browser's local storage.
Indeed the problem raises when I try to show cart items by clicking on the cart link at the navbar section. The error message is 'GET http://localhost:3000/products/undefined 500 (Internal Server Error)' and 'Uncaught (in promise)'. and I don't know how to fix the issue.
Note: the same component 'CartScreen.js' would display the cart items in both cases, when adding new items to the cart & when also clicking on the cart link at the navbar.
Please follow the code snippets
Thanks & Regards
App.js
import Header from './components/Header';
import { Container } from 'react-bootstrap';
import HomeScreen from './screens/HomeScreen';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import ProductScreen from './screens/ProductScreen';
import CartScreen from './screens/CartScreen';
function App() {
return (
<Router>
<Header />
<main className="py-3">
<Container>
<Routes>
<Route path="/" element={<HomeScreen />} exact />
<Route path="/product/">
<Route path=":id" element={<ProductScreen />} />
<Route index element={<ProductScreen />} />
</Route>
<Route path="/cart" >
<Route index element={<CartScreen />} />
<Route path=":productid" element={<CartScreen />} />
</Route>
</Routes>
</Container>
</main>
<Footer />
</Router>
);
}
export default App;
ProductScreen.js
import { useParams, Link, useNavigate } from 'react-router-dom';
import {Row,Col,Image,ListGroup,Button,Card,Form} from 'react-bootstrap';
import Rating from '../components/Rating';
import { listProductDetails } from '../actions/productActions';
import { useDispatch, useSelector } from 'react-redux';
import Loader from '../components/Loader';
import Message from '../components/Message';
function ProductScreen() {
const { id } = useParams();
const navigate = useNavigate();
const [qty, setQty] = useState(1);
const dispatch = useDispatch();
const productListDetail = useSelector((state) => state.productDetail);
const { loading, error, product } = productListDetail;
useEffect(() => {
dispatch(listProductDetails(id));
}, [dispatch, id]);
const addToCartHandler = () => {
navigate(`/cart/${id}?qty=${qty}`);
};
return (
<div> <Link to={-1} className="btn btn-primary my-3">Go Back</Link>
{loading ? (<Loader />): error ? (<Message variant="danger">{error}</Message>) : (
<Row>
<Col md={6}>
<Image src={product.image} alt={product.name} fluid />
</Col>
<Col md={3}>
<ListGroup variant="flush">
<ListGroup.Item>
<h3> {product.name}</h3>
</ListGroup.Item>
<ListGroup.Item>
<Rating value={product.rating} text={`${product.numReviews} reviews`}
color={'#fae500'}/>
</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.Item>
<Row>
<Col> Status: </Col>
<Col>
<strong>
{product.countInStock > 0 ? 'In Stock' : 'Out of Stock'}
</strong>
</Col>
</Row>
</ListGroup.Item>
{product.countInStock > 0 && (
<ListGroup.Item>
<Row>
<Col> Qty </Col>
<Col xs="auto" className="my-1">
<Form.Control as="select" value={qty}
onChange={(e) => setQty(e.target.value)}>
{[...Array(product.countInStock).keys()].map((x) => (
<option key={x + 1} value={x + 1}>{x + 1}</option>))}
</Form.Control>
</Col>
</Row>
</ListGroup.Item>)}
<ListGroup.Item>
<Button onClick={addToCartHandler}
className="btn btn-primary container-fluid"
disabled={product.countInStock === 0}
type="button">
Add to Cart
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
)}
</div>
);
}
export default ProductScreen;
CartScreen.js
import React, { useEffect } from 'react';
import { Col, ListGroup,Row,} from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation, useNavigate, useParams, Link, Outlet } from 'react-router-dom';
import { addToCart } from '../actions/cartAction';
import Message from '../components/Message';
const CartScreen = () => {
const { search } = useLocation();
const { productid } = useParams();
const qty = search ? Number(search.split('=')[1]) : 1;
const dispatch = useDispatch();
const cart = useSelector((state) => state.cart);
const { cartItems } = cart;
useEffect(() => {
dispatch(addToCart(productid, qty));
},[dispatch, productid, qty]);
return (
<Row>
<Col md={8}> {cartItems.length === 0 ? (<Message variant="info">
Go Back To Home Page <Link to="/"></Link> </Message> ) : (
<ListGroup> {cartItems.map((x) => (
<ListGroup.Item key={x.product}>
{x.name} , {x.qty}
</ListGroup.Item> ))}
</ListGroup>)}
</Col>
<Col md={4}></Col>
</Row>
);
};
export default CartScreen;
cartReducers.js
import { CART_ADD_ITEM } from '../constants/cartConstants';
export const cartReducer = (state = { cartItems: [] }, action) => {
switch (action.type) {
case CART_ADD_ITEM:
const item = action.payload;
const existItem = state.cartItems.find((x) => x.product === item.product);
if (existItem) {
return {
...state, cartItems: state.cartItems.map((x) =>
x.product === existItem.product ? item : x),};}
else {
return {
...state, cartItems: [...state.cartItems, item],};}
default:
return state;
}
};
cartAction.js
import axios from 'axios';
import { CART_ADD_ITEM } from '../constants/cartConstants';
export const addToCart = (productid, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/products/${productid}`);
dispatch({
type: CART_ADD_ITEM,
payload: {
product: data._id,
name: data.name,
image: data.image,
price: data.price,
countInStock: data.countInStock,
qty,
},
});
localStorage.setItem('cartItems', JSON.stringify(getState().cart.cartItems));
};
store.js
import { legacy_createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from '@redux-devtools/extension';
import {
productDetailsReducer,
productListReducer,
} from './reducers/productReducers';
import { cartReducer } from './reducers/cartReducers';
const reducer = combineReducers({
productList: productListReducer,
productDetail: productDetailsReducer,
cart: cartReducer,
});
const cartItemsFromStorage = localStorage.getItem('cartItems')
? JSON.parse(localStorage.getItem('cartItems'))
: [];
const initialState = { cart: { cartItems: cartItemsFromStorage } };
const middleware = [thunk];
const store = legacy_createStore(
reducer,
initialState,
composeWithDevTools(applyMiddleware(...middleware))
);
export default store;
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
似乎可以通过在我的答案中使用相同的逻辑来解决此处的问题 - 关于路线匹配的匹配 - 限制 - cartitems-app”>问题。它并未明确称为问题或其他需要解决/固定的(,我以为您想您使用过代码,并且在其他地方遇到了一个问题),因此在此处为特定的Axios问题和解决方案添加答案。
看起来像导航到
“/cart”
将导致productid 和
QTY
是未定义/无效的值,useffect
挂钩无条件地派遣操作以添加项目&amp;数量。productid
在axios.get(`/products/$ {productid}`)
中未定义。如果有有效的产品ID和要添加的数量,则只能派遣
addtocart
操作。It seems the issue here might be resolved by using the same logic in my answer here to your other question regarding route matching. It wasn't explicitly called out as an issue or something the needed to be addressed/fixed (in other words, I thought you'd used the code and had an issue elsewhere), so adding an answer here for the specific axios issue and resolution.
It looks like navigating to
"/cart"
will result in bothproductid
andqty
being undefined/invalid values, and theuseEffect
hook is unconditionally dispatching the action to add the item & quantity.productid
is undefined ataxios.get(`/products/${productid}`)
in the action creator.You should only dispatch the
addToCart
action if there is a valid product id and a quantity to add.