You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js数据获取与组件渲染报错,请求技术帮助

Next.js 数据获取与页面渲染报错排查及解决方案

问题背景

项目从外部API接口 http://back.menumizeme.com/api/get_business_info?business_id=1 获取数据,传递给Services组件后根据URL路径渲染不同页面模块。运行时出现大量报错,以下是针对性的修复方案:


1. ServicesPage 组件:完善数据加载校验

原代码仅判断 !data,未校验API返回数据的深层结构,若data.data或data.data[0]不存在,会导致后续组件直接报错。

修复代码:

import React, { useEffect, useState } from "react";
import Services from "../../components/Services/Services";

const ServicesPage = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(
          "http://back.menumizeme.com/api/get_business_info?business_id=1"
        );
        const data = await response.json();
        setData(data);
      } catch (error) {
        console.error("Error fetching data", error);
      }
    }

    fetchData();
  }, []);

  // 增加多层数据结构校验
  if (!data || !data.data || !data.data[0]) {
    return <div>Loading...</div>;
  }

  return (
    <div className="container h-auto">
      <Services data={data} />
    </div>
  );
};

export default ServicesPage;

2. Services 组件:恢复数据校验+修正数据路径

原代码注释了数据校验逻辑,且未处理嵌套对象的空值风险;同时Header组件的props传递后,数据访问路径存在错误。

修复代码:

import Image from "next/image";
import Header from "./Header";
import CategorieItem from "@/components/Services/Categorie-Item";
import Categories from "./Categories-List";
import { useRouter } from "next/router";
import MyOrder from "./MyOrder";

const Services = ({ data }) => {
  const serverBaseURI = "http://back.menumizeme.com";
  const router = useRouter();

  // 恢复数据校验逻辑
  if (!data || !data.data || !data.data[0]) {
    return <div>Loading....</div>;
  }
  const item = data.data[0];

  return (
    <section className="flex">
      {/* 使用可选链避免空值报错 */}
      <Image
        src={`${serverBaseURI}/storage/${item.business?.business_logo || ""}`}
        className="object-cover w-screen h-full"
        alt="Business Logo"
        width={1000}
        height={1000}
      />
      <div className="absolute flex flex-col w-full">
        <Header item={item} url={serverBaseURI} />
        {router.pathname === "/services" && (
          <div className="mt-3 ">
            {/* 使用可选链避免map空值报错 */}
            {item.category_infos?.map((category, index) => (
              <Categories key={index} category={category} item={item} />
            ))}
          </div>
        )}

        {/* 补全分类项页面渲染逻辑 */}
        {router.pathname.startsWith("/services/") && (
          <div className="mt-3">
            <CategorieItem restaurant={item} />
          </div>
        )}
        {router.pathname === "/myOrder/myOrder" && (
          <div className="mt-3">
            <MyOrder />
          </div>
        )}
      </div>
    </section>
  );
};

export default Services;

3. Header 组件:修正数据访问路径+Image组件错误

原代码中数据访问路径错误(item.data.business_logo应为item.business?.business_logo),且存在className拼写错误、Image组件尺寸不合理问题。

修复代码:

import React, { useState } from "react";
import { useRouter } from "next/router";
import { menuLink } from "./constants";
import Image from "next/image";
import { close, menu, basket } from "./assets";
import { MdLocationOn, MdPhone } from "react-icons/md";

const Header = ({ item, url }) => {
  const router = useRouter();
  const [toggle, setToggle] = useState(false);

  return (
    <div className="flex flex-row w-full justify-between">
      <div className="flex">
        <Image
          src={toggle ? close : menu}
          alt="menu"
          className="w-[32px] h-[32px] mr-1 object-contain ml-[30px] mt-12 cursor-pointer"
          onClick={() => setToggle((prev) => !prev)}
        />
        <div
          className={`${
            toggle ? "flex" : "hidden"
          } bg-[#000000f1] absolute top-20 w-[98.5vw] sidebar z-[1]`}
        >
          <ul className="list-none flex flex-col justify-end items-center flex-1">
            {menuLink.map((nav, index) => (
              <li
                key={nav.id}
                className={`font-poppins font-semibold border-[#ffffff83] w-[98.5vw] cursor-pointer flex flex-col justify-center h-12 text-[18px] leading-7 text-white text-center hover:text-[#FAB25A] hover:font-bold hover:bg-[#fab25a2d] ${
                  index === menuLink.length - 1 ? "" : "border-b-[1px]"
                }`}
              >
                <a href={`#${nav.id}`}>{nav.title}</a>
              </li>
            ))}
          </ul>
        </div>
      </div>
      <div className="mt-10 flex flex-col">
        {/* 修正数据路径+拼写错误+合理尺寸 */}
        <Image
          src={`${url}/storage/${item.business?.business_logo || ""}`}
          className="object-contain"
          alt="Business Logo"
          width={100}
          height={100}
        />
        {router.pathname === "/services" && (
          <p className="font-Montserrat font-[700] text-lg text-[#FEFEFE] capitalize">
            {item.name} {item.businessType}
          </p>
        )}
        {router.pathname === "/services" && (
          <div className="flex-col mt-14 mx-2">
            <div className="flex flex-row mt-2">
              <MdLocationOn
                color="#A8A8A8"
                className="w-[20px] h-[20px] mt-1 mr-2"
              />
              <p className="font-Montserrat font-[400] text-lg text-[#A8A8A8]">
                {item.address}
              </p>
            </div>
            <div className="flex flex-row mt-2">
              <MdPhone
                color="#A8A8A8"
                className="w-[20px] h-[20px] mt-1 mr-2"
              />
              <p className="font-Montserrat font-[400] text-lg text-[#A8A8A8]">
                {item.phone_number}
              </p>
            </div>
          </div>
        )}
      </div>
      <div className="mr-[30px] mt-12">
        <Image src={basket} className="cursor-pointer" alt="Basket" />
      </div>
    </div>
  );
};

export default Header;

4. CategorieItem 组件:修正props拼写+数据逻辑+Image组件

原组件存在props拼写错误(resturant→restaurant)、依赖硬编码常量而非API数据、Image组件缺少容器适配问题。

修复代码:

import Image from "next/image";
import { useRouter } from "next/router";
import React, { useEffect, useState } from "react";
import ModalOrder from "./Modal/ModalOrder";

// 修正props拼写
const CategorieItem = ({ restaurant }) => {
  const { query } = useRouter();
  const [productsToShow, setProductsToShow] = useState([]);
  const [showModal, setShowModal] = useState(false);
  
  useEffect(() => {
    // 增加空值校验
    if (!restaurant?.categories || !query?.params) {
      setProductsToShow([]);
      return;
    }
    const matchedCategory = restaurant.categories.find(
      (category) => parseInt(category.id) === parseInt(query.params[0])
    );
    setProductsToShow(matchedCategory?.products || []);
  }, [query, restaurant]);

  return (
    <section>
      <div className="flex flex-col">
        {productsToShow?.map((product, index) => (
          // 将key移至外层div
          <div key={index}>
            <div
              className="flex flex-row w-[22rem] h-[11rem] justify-between items-center cursor-pointer mt-4 mx-auto "
              onClick={() => setShowModal(true)}
            >
              {/* 增加relative容器配合fill属性 */}
              <div className="relative w-[11rem] h-[11rem]">
                <Image
                  src={product.image}
                  className="cursor-pointer transition duration-300 ease-in-out hover:scale-110 rounded-full"
                  alt={product.name}
                  fill
                  objectFit="cover"
                />
              </div>
              <div className="flex flex-col justify-center pl-9 items-start w-[13rem] h-[8rem] bg-gradient-to-l from-[#ffffffb7] rounded-r-[13rem] my-auto ">
                <h2 className="font-Montserrat text-[#fff] font-bold text-lg leading-5">
                  {product.name}
                </h2>
                <p className="font-Montserrat text-[#fff] font-[500] text-[12px] leading-4 my-2">
                  {product.description}
                </p>
                <p className="font-Montserrat text-[#fff] font-bold text-lg leading-5">
                  {product.price}
                </p>
              </div>
            </div>
            <ModalOrder product={product} showModal={showModal} setShowModal={setShowModal}/>
          </div>
        ))}
      </div>
    </section>
  );
};

export default CategorieItem;

5. 全局配置:Next.js 外部图片域名

在next.config.js中配置外部图片域名,避免Image组件报错:

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    domains: ['back.menumizeme.com'],
  },
}

module.exports = nextConfig;

内容的提问来源于stack exchange,提问作者Zakaria Zhlat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:38:11