Next.js 应用错误:发生了一个客户端异常(请查看浏览器控制台获取更多信息)

12
在尝试加载从外部 API 拉取的产品信息并呈现的页面时,如果出现上述错误,则仅在生产环境中发生错误,本地一切正常。检查控制台时,我得到了这个错误:
TypeError: undefined is not a function (near '...n.map...')

"Next Build" 可以正常工作并生成生产环境的构建版本。 "Next Start" 也能正常工作,并且控制台或其他地方都没有抛出错误。只有在推送到生产环境后才会出现错误。对于发生的情况感到困惑。以下是呈现产品列表的组件代码:
import type { NextPage } from 'next'
import React, { useState, useEffect, useContext} from 'react';
import styles from '../styles/Home.module.css'
import DepopIcon from './icons/depop.svg'
import { Image, Main, Heading, Paragraph, Nav, Icons, Header, Grommet, Box, Button, Grid, Text, Footer, Anchor } from 'grommet';
import {
  Instagram,
  Shop,
  Cart
} from 'grommet-icons';
import { useRouter } from 'next/router';

export default function ProductsList() {
    const router = useRouter();
    // load our products state variable
    const [products, setProducts] = useState([]);
    const [prodImg, setProdImg] = useState([]);


    function handleProdClick(id){
        router.push({ pathname: "/product", query: { id: id } })
    }

    const getProducts = async () => {
         const response = await fetch("/api/getproducts");
         var data = await response.json();
         data = JSON.parse(data);
         await setProducts(data);
    }

    useEffect(() => {
            getProducts();
        }, [])
    //

    // build areas list for our grid
    const areaVar = "area";
    const cols = 3;
    var r = 0;
    var c = 0;
    var areas = []
    for (let x = 1; x < products.length+1; x++){
        const name = areaVar+x.toString()
        areas.push({ name: name, start: [r,c], end: [r,c]  })
        r += 1
        if (x % 3 == 0){
            r = 0
            c += 1
        }
    }
    //

    console.log(products);

    // create our product objects for our grid
    var productObjs = [];
    if (typeof products != "undefined" && products != null){
        productObjs = products.map((product, index) => {
            return(
            <Box
              key={product._id}
              gridArea= {areas[index].name}
              background="#003311"
              direction="column"
              pad="none"
              align="center"
              onClick={() => {handleProdClick(product._id)}}
            >
                <Image
                  src={product.frontImage} fill={true} fit="cover"
                  onMouseOver={e => (e.currentTarget.src = product.backImage)}
                  onMouseOut={e => (e.currentTarget.src = product.frontImage)}
                />
                <Box
                  direction="row"
                  pad="xsmall"
                  align="center"
                  gap="large"
                  justify="between"
                  animation="fadeIn"
                >
                  <Text color="#fff" size="medium">{product.name}</Text>
                  <Text color="#fff" size="medium">{product.color}</Text>
                  <Text color="#fff" size="medium">{product.price} USD</Text>
                  <Text color="#fff" size="medium">{product.size}</Text>
                </Box>
           </Box>
            );
        });
    }

    //
    const rows = []
    for (let i = 0; i < r; i++){
        rows.push('large')
    }
    return (
        <Grid
          rows={rows}
          columns={['flex', 'flex', 'flex']}
          gap="none"
          areas={areas}
        >
          {productObjs}
        </Grid>
    );
}

使用上述组件的页面:
import type { NextPage } from 'next'
import React, { useState, useContext, useEffect} from 'react';
import { Paragraph, Main, Nav, Icons, Header, Grommet, Box, Button, Grid, Text, Footer, Anchor } from 'grommet';
import ContactHeaderUnit from '../components/ContactHeaderUnit.tsx'
import FooterUnit from '../components/FooterUnit.tsx'
import ProductsList from '../components/ProductsList.tsx'

const Shop: NextPage = () => {

      return (
          <Grommet
              full={true}
              background="#fffced"
              theme={{
                  global: { font: {
                              family: "Alice"
                            }},
                paragraph: {
                    font: {
                      family: 'Arial',
                    },
                },
                label: {
                    font: {
                      family: 'Arial',
                    },
                  },
              }}
          >
            <ContactHeaderUnit/>
            <ProductsList/>
            <FooterUnit/>
          </Grommet>
      )
}

export default Shop;

2
看起来 products 没有 .map 函数,这意味着你在生产环境中设置的内容不是一个数组。也许在生产环境中,你的请求被远程服务器阻止或以与本地不同的方式更改。检查设置 products 时的数据类型,以便进行观察。你可以在那段代码中设置断点、记录日志等。如果它不是一个数组,你真的不应该设置那个状态,并且像 (typeof products != "undefined" && products != null) 这样的检查似乎是多余的,如果你在设置无效状态之前进行了类型检查。 - Yuji 'Tomita' Tomita
10个回答

16

我曾经遇到这个错误,即使在编译时没有出现错误,但找出原因非常困难,花费了几天的时间。最后发现一些传入数据渲染成了无效的HTML(动态创建脚本标记出了问题)。我通过试错法解决了这个错误,逐步移除不同的传入数据,并查看页面是否能够正确渲染。如果我再次遇到此类问题,我会从这里开始尝试。


4
有没有办法更改/自定义该消息? - xgeek652
在我的情况下,有一个导航组件,根据user.id进行导航,当有经过身份验证的用户时。但是,当没有经过身份验证时,它会抛出这个错误。所以我做的是使用问号,像这样user?.id,这样我的问题就解决了。 - Ahmed fouad

1

我曾经遇到相同的错误,但问题可能有很多原因。建议首先检查您的HTML标签。在dev模式下运行应用程序并仔细查看控制台中的错误也是有用的。在我的情况下,问题出在从父组件传递props到子组件和子子组件时传递不正确,我将函数调用传递给了props。通过在最终子组件中创建一个不需要传递的函数来解决了这个问题。希望这可以帮助到你。


1

0

我遇到了这个错误,以下是在我的项目中帮助我的方法。

来自我的暂存网站(Heroku)的屏幕截图(本地没有错误)

enter image description here

在我的本地开发模式下,我已将 API 更改为从 Staging 站点获取,而不是本地获取(我正在使用 Strapi CMS)

NEXT_PUBLIC_STRAPI_API='https://XXXXXXXXX.herokuapp.com/api'

我在本地出现了错误,它给了我更详细的错误信息,在我这种情况下使用链接

enter image description here

为了找到错误的来源,在Layout.js文件中,我逐一取消注释了1)Header,2)main,3)Footer。最终发现是Footer引起了错误,修复了Footer后,错误不再出现。

enter image description here


0
对我来说,我将一个空对象传递给了一个图像组件,并将其断言为一个字符串。在生产环境中,它会产生客户端错误。源代码非常混淆,但如果你仔细查找,应该会有一些提示,告诉你出了什么问题。

0
对我来说,这是一个我正在使用的cookie同意库。如果您也在使用,请检查一下。

0

从我在生产环境中遇到这个错误信息的经验来看,通常是因为尝试访问空对象、未定义的对象或甚至是对象中不存在的props/functions。 避免这些类型的错误的一种方法是使用typescript :)


0

对我来说,错误是因为我在 Vercel 仪表板上错过了环境变量。我只有 .env 文件。在仪表板上更新环境变量后,一切正常。


0
你是否已经在设置中将.local.env文件添加到你的Vercel项目中?从经验来看,如果你没有在生产环境中添加.local.env文件,那么通常会出现客户端错误(请确保你已经将所有的环境变量复制到剪贴板上)。要做到这一点,请转到你的Vercel项目仪表板->设置->环境变量->单击“Key”文本字段并粘贴你复制的env->检查生产、预览和部署->单击保存->转到你的部署并将最新的(或“准备好”的)推广到生产环境。希望这可以帮助你。

0
在我的情况下,我安装了一个可能与Next 12/React 18不兼容的包。具体来说,我发现Headless UI是罪魁祸首,可以在GitHub issue中找到相关信息。其中一个选择是降级到React 17,但我并不真正想这么做。
对我来说解决问题的方法是禁用React的严格模式,就像这样:
const nextConfig = {
  reactStrictMode: false,
  // other config here...
};

module.exports = nextConfig;

希望这对某人有所帮助 :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接