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

