使用React-Router-Dom V6的Cartitems路由误差

发布于 2025-02-13 17:31:09 字数 9100 浏览 0 评论 0原文

我正在尝试将“ 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;

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

爱本泡沫多脆弱 2025-02-20 17:31:09

似乎可以通过在我的答案中使用相同的逻辑来解决此处的问题 - 关于路线匹配的匹配 - 限制 - cartitems-app”>问题。它并未明确称为问题或其他需要解决/固定的,我以为您想您使用过代码,并且在其他地方遇到了一个问题),因此在此处为特定的Axios问题和解决方案添加答案。

看起来像导航到“/cart”将导致 productid QTY是未定义/无效的值,useffect挂钩无条件地派遣操作以添加项目&amp;数量。 productidaxios.get(`/products/$ {productid}`)中未定义。

如果有有效的产品ID和要添加的数量,则只能派遣addtocart操作。

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(() => {
    if (productid && qty > 0) {
      dispatch(addToCart(productid, qty)); // <-- only dispatch if valid
    }
  }, [dispatch, productid, qty]);

  return ( 
    <Row>
      <Col md={8}>
        {!cartItems.length
          ? (
            <Message variant="info">
              <Link to="/">
                Go Back To Home Page
              </Link>
            </Message>
          ) : (
            <ListGroup>
              {cartItems.map((x) => (
                <ListGroup.Item key={x.product}>
                  {x.name} , {x.qty}
                </ListGroup.Item>
              ))}
            </ListGroup>
          )
        }
      </Col>
      <Col md={4}></Col>
    </Row>
  );
};

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 both productid and qty being undefined/invalid values, and the useEffect hook is unconditionally dispatching the action to add the item & quantity. productid is undefined at axios.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.

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(() => {
    if (productid && qty > 0) {
      dispatch(addToCart(productid, qty)); // <-- only dispatch if valid
    }
  }, [dispatch, productid, qty]);

  return ( 
    <Row>
      <Col md={8}>
        {!cartItems.length
          ? (
            <Message variant="info">
              <Link to="/">
                Go Back To Home Page
              </Link>
            </Message>
          ) : (
            <ListGroup>
              {cartItems.map((x) => (
                <ListGroup.Item key={x.product}>
                  {x.name} , {x.qty}
                </ListGroup.Item>
              ))}
            </ListGroup>
          )
        }
      </Col>
      <Col md={4}></Col>
    </Row>
  );
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文