【发布时间】:2021-03-23 09:11:14
【问题描述】:
我正在学习一个教程,我们正在构建一个尚未完成的购物车,但我想在继续学习教程之前解决这个问题。
问题是,当我尝试将任何商品添加到购物车时,购物车仍然在说我的购物车是空的。您可以在下面这张图片的 url 中注意到,我已将 id 编号为 1 的商品添加到购物车中,并且数量 (qty) 也是 1。
但购物车在添加商品后不应响应此类消息。在代码所在的这个阶段,响应应该只是一个空白页。
当我尝试控制台记录 cartItems 时,这还没有奏效 - 这对我来说是一个空数组,但讲师正在接收有关产品的数据,这些数据已添加到购物车中。所以这可能是一个线索。
请查看以下文件,如果您能看到任何提示或解决方案,请告诉我。
CartScreen.js -
import React, {useEffect} from 'react'
import { Link } from 'react-router-dom'
import { useDispatch, useSelector } from 'react-redux'
import { Row, Col, ListGroup, Image, Form, Button, Card } from 'react-bootstrap'
import Message from '../components/Message'
import { addToCart } from '../actions/cartActions'
function CartScreen({ match, location, history }) {
const productId = match.params.div
const qty = location.search ? Number(location.search.split('=')[1]) : 1
const dispatch = useDispatch()
const cart = useSelector(state => state.cart)
const { cartItems } = cart
useEffect(() =>{
if(productId){
dispatch(addToCart(productId, qty))
}
}, [dispatch, productId, qty])
return (
<Row>
<Col md={8}>
<h1>Shopping Cart</h1>
{cartItems.length === 0 ? (
<Message variant='info'>
Your cart is empty <Link to='/'>Go Back</Link>
</Message>
) : (
<listGroup variant='flush'>
</listGroup>
)}
</Col>
<Col md={4}>
</Col>
</Row>
)
}
export default CartScreen
cartActions.js -
import axios from 'axios'
import { CART_ADD_ITEM } from '../constants/cartConstants'
export const addToCart = (id, qty) => async (dispatch, getState) => {
const { data } = await axios.get(`/api/products/${id}`)
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))
}
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
}
}
cartConstants.js -
export const CART_ADD_ITEM = 'CART_ADD_ITEM'
export const CART_REMOVE_ITEM = 'CART_REMOVE_ITEM'
store.js -
import { createStore, combineReducers, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { composeWithDevTools } from 'redux-devtools-extension'
import { productListReducer, productDetailsReducer } from './reducers/productReducers'
import { cartReducer } from './reducers/cartReducers'
const reducer = combineReducers({
productList:productListReducer,
productDetails:productDetailsReducer,
cart: cartReducer,
})
const cartItemsFromStorage = localStorage.getItem('cartItems') ?
JSON.parse(localStorage.getItem('cartItems')) : []
const initialState = {
cart: { cartItems: cartItemsFromStorage }
}
const middleware = [thunk]
const store = createStore(reducer, initialState,
composeWithDevTools(applyMiddleware(...middleware)))
export default store
ProductScreen.js -
import React, { useState, useEffect } from "react";
import { useDispatch, useSelector } from 'react-redux'
import { Link } from "react-router-dom";
import { Row, Col, Image, ListGroup, Button, Card, Form } from "react-bootstrap";
import Rating from "../components/Rating";
import Loader from "../components/Loader";
import Message from "../components/Message";
import { listProductDetails } from '../actions/productActions'
function ProductScreen({ match, history }) {
const[qty, setQty] = useState(1)
const dispatch = useDispatch()
const productDetails = useSelector(state => state.productDetails)
const { loading, error, product } = productDetails
useEffect(() => {
dispatch(listProductDetails(match.params.id))
},[dispatch, match])
const addToCartHandler = () => {
history.push(`/cart/${match.params.id}?qty=${qty}`)
}
return (
<div>
<Link to="/" className="btn btn-light 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={"#f8e825"}
/>
</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>
{product.countInStock > 0 ? "In Stock" : "Out of Stock"}
</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-block"
disabled={product.countInStock == 0}
type="button"
>
Add to Cart
</Button>
</ListGroup.Item>
</ListGroup>
</Card>
</Col>
</Row>
)
}
</div>
);
}
export default ProductScreen;
【问题讨论】:
-
问题不在于您目前显示的代码。它位于您将商品添加到购物车然后导航到购物车的代码中。我怀疑您正在发送将商品添加到购物车的操作,而您并没有等待它解决。调度一个动作是一个承诺并且是异步的。您必须等到它解决,这与同步的提交不同。这可以解释为什么您在购物车仍然是空的时候到达购物车页面。
-
您好,我在这篇文章中添加了两个附加文件 - store.js 和 ProductScreen.js。您是否认为问题的解决方案可能隐藏在其中的某个地方?非常感谢您的提示,但我不知道如何将这些知识转化为代码本身。
-
index.js 在我们构建购物车时没有受到影响,但也许它会对您有所帮助。 index.js - 从 'react' 导入 React;从 'react-dom' 导入 ReactDOM;从 'react-redux' 导入 { Provider } 从 './store' 导入存储 './index.css';导入'./bootstrap.min.css';从'./App'导入应用程序;从'./reportWebVitals'导入reportWebVitals; ReactDOM.render(
, document.getElementById('root') );报告WebVitals();
标签: javascript reactjs redux react-redux shopping-cart