首页 > 解决方案 > 在 React 应用程序中找不到错误

问题描述

我一直在用 React 编写一个应用程序(电子商务,作为一个项目,遵循一个教程)。引用购物车对象时,我收到“TypeError:无法读取未定义的属性“长度””的错误。这是一些背景背景。我正在使用 App.js 组件顶部附近的 useState 挂钩生成购物车对象:

const [cart, setCart] = useState({});

在 App.js 再往下一点,console.log 语句执行时没有错误提示购物车对象存在:

console.log(cart);

但是,当我尝试将购物车对象传递给 App.js 渲染部分中的购物车组件时,会生成上述错误(例如“TypeError:无法读取未定义的属性“长度”)。为什么会发生这种情况,我该如何解决?

这是 App.js 的代码

import React, { useState, useEffect } from 'react'
import { commerce } from './lib/commerce';

import { Products, Navbar, Cart } from './components';

const App = () => {
    const [products, setProducts] = useState([]);
    const [cart, setCart] = useState({});

    const fetchProducts = async () => {
        const { data } = await commerce.products.list();
        setProducts(data);
    }

    const fetchCart = async () => {
        setCart(await commerce.cart.retrieve());
    }

    const handleAddToCart = async (productID, quantity) => {
        const item = await commerce.cart.add(productID, quantity);
        setCart(item.cart);
    }

    useEffect(() => {
        fetchProducts();
        fetchCart();
    }, []);

    
    console.log(cart);
    return (
        <div>
            <Navbar totalItems={cart.total_items} />
            {/* <Products products={products} onAddToCart={handleAddToCart} /> */}
            <Cart cart={cart} />
        </div>
    )
}

export default App

这是我将购物车对象传递到的组件(购物车)的代码:

import React from 'react'
import { Container, Typography, Button, Grid } from "@material-ui/core";
import useStyles from './styles';

const Cart = ({ cart }) => {
    const isEmpty = !cart.line_items.length;
    const classes = useStyles();

    const EmptyCart = () => {
        <Typography variant="subtitle1">
            You have no items your shopping cart..
        </Typography>
    }

    const FilledCart = () => {
        <>
            <Grid container spacing={3}>
                {
                    cart.line_items.map((item) => (
                        <Grid item xs={12} sm={4} key={item.id}>
                            <div>{item.name}</div>
                        </Grid>
                    ))
                }
            </Grid>
            <div className={classes.cardDetails}>
                <Typography variant="h4">
                    Subtotal: { cart.subtotal.formatted_with_symbol }
                </Typography>
                <div>
                    <Button className={classes.emptyButton} size="large" variant="contained" type="button" color="secondary">Empty Cart</Button>
                    <Button className={classes.checkoutButton} size="large" variant="contained" type="button" color="primary">Checkout</Button>
                </div>
            </div>
        </>
    }
    return (
        <Container>
            <div className={classes.toolbar} />
            <Typography className={classes.title} variant="h3">Your shopping cart</Typography>            
            {
                isEmpty ? <EmptyCart /> : <FilledCart />
            }
        </Container>
    )
}

export default Cart

标签: javascriptreactjs

解决方案


问题

您的代码的问题是初始状态与您在初始渲染中访问的内容不匹配。

  1. App组件中,cart状态只是一个空对象。

    const [cart, setCart] = useState({});
    
  2. cart作为 props 传递给Cart组件,并且代码假定cart.line_items是为了访问length属性或map函数而定义的。cart.line_itemsOFC 未定义,因此尝试访问该length属性 (map) 会抛出TypeError: Cannot read property 'XYZ' of undefined

    const isEmpty = !cart.line_items.length;
    

    cart.line_items.map(.....
    

但是当我在 App.js 中 console.log 时,它实际上会打印出必要的信息。

console.log(cart);位于组件的函数体中,因此它错误地将cart状态记录为无意的副作用,应该从useEffect挂钩中记录它,以便您查看每个渲染周期的值。这里的另一个问题是您没有访问任何嵌套属性,因此这永远不会引发错误。我敢打赌,使用代码,您至少有 1 或 2 个日志条目,它们只是空对象 ( {}) ,然后您会看到一些具有填充嵌套属性的日志。

可能的日志输出示例:

{}
{}
{ line_items: [.....], subtotal: ..... }
{ line_items: [.....], subtotal: ..... }

解决方案

关于状态记录,您应该使用useEffect依赖于您正在记录的状态值的挂钩。这将cart在初始渲染时记录状态,稍后仅在cart更新状态值时记录。

useEffect(() => {
  console.log(cart);
}, [cart]);

对于错误,您有几个选项可以帮助防止在访问您的cart状态时出现错误。

  1. 提供与渲染周期中访问的内容相匹配的有效初始状态,添加line_items: []到初始cart状态,使其cart.line_items现在存在并具有length属性。

    const [cart, setCart] = useState({ line_items: [] });
    
  2. cart在传递的道具上使用保护子句或可选链接。

    const isEmpty = !(cart.line_items && cart.line_items.length);
    

    或者

    const isEmpty = !cart.line_items?.length);
    

    cart.line_items && cart.line_items.map(.....
    

    或者

    cart.line_items?.map(.....
    

    cart.subtotal在未定义的情况下也可以保护小计子状态。

    <Typography variant="h4">
      Subtotal: { cart.subtotal?.formatted_with_symbol }
    </Typography>
    

推荐阅读