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
相关产品推荐
相关产品推荐

