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

ReactJS中如何在渲染前完成API数据获取?

React + Strapi 博客渲染报错:数据加载前访问属性导致未定义错误

我用Strapi作为CMS搭建简易React博客时遇到问题:组件会在API数据获取完成前就尝试渲染内容,访问data.data.attributes.image.data.attributes.formats.large.url时出现attributes未定义的错误。

我的代码

React组件代码

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Navbar from "../components/Navbar";

function Post() {
    const [data, setData] = useState({});
    const { id } = useParams();

    console.log("handle", id);

    const fetchData = () => {
        fetch(`http://localhost:1337/api/posts/${id}?populate=*`)
            .then((response) => {
                return response.json();
            })
            .then((d) => {
                setData(d);
                // console.log(data);
            });
    };

    useEffect(() => {
        fetchData();
    }, []);

    // console.log(data);

    return (
        <>
            <Navbar />
            <img
                src={
                    "http://localhost:1337" +
                    data.data.attributes.image.data.attributes.formats.large.url
                }
                alt="image"
            />
        </>
    );
}

export default Post;

Strapi返回的API数据

{
    "data": {
        "id": 1,
        "attributes": {
            "postTitle": "First Post",
            "datePosted": "2023-04-25",
            "author": "Caleb",
            "postText": "# Hello <u>**_World_**</u>\nThis is my very first blog post!\n\n![flower-right.png](http://localhost:1337/uploads/flower_right_b36ad9cd80.png)\n\n",
            "createdAt": "2023-04-25T07:20:18.295Z",
            "updatedAt": "2023-04-25T08:44:04.003Z",
            "publishedAt": "2023-04-25T07:20:19.972Z",
            "image": {
                "data": {
                    "id": 1,
                    "attributes": {
                        "name": "unsplash_BhfE1IgcsA8-smaller.png",
                        "alternativeText": "header-image",
                        "caption": null,
                        "width": 2560,
                        "height": 1488,
                        "formats": {
                            "thumbnail": {
                                "name": "thumbnail_unsplash_BhfE1IgcsA8-smaller.png",
                                "hash": "thumbnail_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d",
                                "ext": ".png",
                                "mime": "image/png",
                                "path": null,
                                "width": 245,
                                "height": 142,
                                "size": 57.28,
                                "url": "/uploads/thumbnail_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d.png"
                            },
                            "small": {
                                "name": "small_unsplash_BhfE1IgcsA8-smaller.png",
                                "hash": "small_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d",
                                "ext": ".png",
                                "mime": "image/png",
                                "path": null,
                                "width": 500,
                                "height": 291,
                                "size": 222.62,
                                "url": "/uploads/small_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d.png"
                            },
                            "medium": {
                                "name": "medium_unsplash_BhfE1IgcsA8-smaller.png",
                                "hash": "medium_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d",
                                "ext": ".png",
                                "mime": "image/png",
                                "path": null,
                                "width": 750,
                                "height": 436,
                                "size": 490.23,
                                "url": "/uploads/medium_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d.png"
                            },
                            "large": {
                                "name": "large_unsplash_BhfE1IgcsA8-smaller.png",
                                "hash": "large_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d",
                                "ext": ".png",
                                "mime": "image/png",
                                "path": null,
                                "width": 1000,
                                "height": 581,
                                "size": 884.48,
                                "url": "/uploads/large_unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d.png"
                            }
                        },
                        "hash": "unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d",
                        "ext": ".png",
                        "mime": "image/png",
                        "size": 1276.78,
                        "url": "/uploads/unsplash_Bhf_E1_Igcs_A8_smaller_b163301e2d.png",
                        "previewUrl": null,
                        "provider": "local",
                        "provider_metadata": null,
                        "createdAt": "2023-04-25T06:28:01.746Z",
                        "updatedAt": "2023-04-25T06:35:42.822Z"
                    }
                }
            }
        }
    },
    "meta": {}
}

解决办法

1. 加条件渲染,等待数据加载完成

初始化状态为null,在数据没加载完时显示加载提示,加载完成后再渲染图片:

import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import Navbar from "../components/Navbar";

function Post() {
    const [data, setData] = useState(null); // 初始化为null
    const { id } = useParams();

    const fetchData = () => {
        fetch(`http://localhost:1337/api/posts/${id}?populate=*`)
            .then((response) => response.json())
            .then((d) => setData(d));
    };

    useEffect(() => {
        fetchData();
    }, [id]); // 把id加入依赖,路由参数变化时重新请求

    // 数据未加载时显示加载状态
    if (!data) {
        return (
            <>
                <Navbar />
                <div>加载中...</div>
            </>
        );
    }

    return (
        <>
            <Navbar />
            <img
                src={"http://localhost:1337" + data.data.attributes.image.data.attributes.formats.large.url}
                alt={data.data.attributes.image.data.attributes.alternativeText || "文章图片"}
            />
        </>
    );
}

export default Post;

2. 使用可选链操作符避免报错

用?.直接在属性访问时做判断,即使某个层级属性不存在也不会报错,还可以搭配??设置默认图:

// ...其他代码不变
return (
    <>
        <Navbar />
        <img
            src={"http://localhost:1337" + (data?.data?.attributes?.image?.data?.attributes?.formats?.large?.url ?? "/default-image.png")}
            alt={data?.data?.attributes?.image?.data?.attributes?.alternativeText ?? "文章图片"}
        />
    </>
);

3. 两种方法结合(推荐)

既加加载状态,又用可选链做兜底,避免极端情况(比如接口返回数据结构异常):

// ...其他代码不变
if (!data) {
    return (
        <>
            <Navbar />
            <div>加载中...</div>
        </>
    );
}

const imageUrl = data?.data?.attributes?.image?.data?.attributes?.formats?.large?.url;
if (!imageUrl) {
    return (
        <>
            <Navbar />
            <div>图片加载失败</div>
        </>
    );
}

return (
    <>
        <Navbar />
        <img
            src={"http://localhost:1337" + imageUrl}
            alt={data.data.attributes.image.data.attributes.alternativeText || "文章图片"}
        />
    </>
);

内容的提问来源于stack exchange,提问作者Caleb Gross

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 04:27:03