【问题标题】:(React and Redux) Problem with adding items to the shopping cart(React 和 Redux)将商品添加到购物车的问题
【发布时间】: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


【解决方案1】:

这是错误:

const productId = match.params.div

它应该是“id”而不是 div

const productId = match.params.id

【讨论】:

  • 我真的很感谢您,亲爱的先生。非常感谢您的非凡观察。这解决了我的问题,因此我可以继续学习,这真是太棒了,因为我期待着建立商店的另一部分。 :-)
  • @JanKoci 祝你好运。不要着急,这就是你犯这个错误的原因
【解决方案2】:

在 cartReducers.js 中,您在更新状态时使用 state.cartItems,而在 CartScreen.js 中,您正在使用 const cart = useSelector(state =&gt; state.cart)

我看不到 index.js,但看起来您的州名称可能不一致。建议:useSelector(state =&gt; state.cartItems)

【讨论】:

  • 您好,感谢您的帮助。如果我尝试更改该代码,则会收到以下错误 - TypeError:无法解构“cart”的属性“cartItems”,因为它未定义。讲师正在使用 const cart = useSelector(state => state.cart) 并且对他有用。
  • 我用 2 个新文件更新了这篇文章 - store.js 和 HomeScreen.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();
猜你喜欢
  • 1970-01-01
  • 2016-12-19
  • 2020-01-14
  • 1970-01-01
  • 1970-01-01
  • 2015-03-31
  • 2018-04-10
相关资源
最近更新 更多