Strapi+React博客开发:Fetch API状态存储与数据访问疑问
React对接Strapi API状态访问异常的解决方法
用Strapi作为CMS、React作为前端搭建博客时,碰到了一个问题:将完整API响应JSON通过setPosts(json)存入state后,无法通过posts.data[0].attributes.Title访问标题数据;但如果仅存入attributes部分setPosts(json.data[0].attributes),就能通过posts.Title正常获取内容。
完整代码
import React, { useState, useEffect } from "react"; const App = () => { const [posts, setPosts] = useState({}); useEffect(() => { const url = "http://localhost:1337/api/blogs"; const options = { headers: { Authorization: process.env.REACT_APP_STRAPI_TOKEN, }, }; const fetchData = async () => { try { const res = await fetch(url, options); const json = await res.json(); // 可行但希望能直接用setPosts(json) setPosts(json.data[0].attributes); console.log(json); } catch (err) { console.log(err); } }; fetchData(); }, []); return <div className="test">{posts.Title}</div>; }; export default App;
Postman返回的完整API响应
{ "data": [ { "id": 1, "attributes": { "Title": "First Blog Post", "CreationDate": "2023-03-29", "Content": "My first blog test. Grabbing API endpoints.\n\nimage in the middle of the text.\n```\ntest code\n```\n\n\n", "createdAt": "2023-03-30T02:01:59.255Z", "updatedAt": "2023-03-30T02:02:06.214Z", "publishedAt": "2023-03-30T02:02:06.123Z" } }, { "id": 2, "attributes": { "Title": "Blog 2 ", "CreationDate": "2023-04-01", "Content": "test content 2", "createdAt": "2023-04-02T20:53:26.950Z", "updatedAt": "2023-04-02T20:53:30.963Z", "publishedAt": "2023-04-02T20:53:30.884Z" } } ], "meta": { "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 2 } } }
问题原因
核心是React初始状态与异步渲染的冲突:
- 初始state设为
{},组件第一次渲染时posts是空对象,此时访问posts.data会得到undefined,继续访问[0].attributes.Title会直接抛出Cannot read properties of undefined的错误,导致组件渲染失败。 - 而仅存attributes时,第一次渲染
posts.Title是undefined,但不会触发报错,等异步请求完成state更新后,组件重新渲染就能拿到正确值。
解决方法
1. 初始化匹配API结构的state
把初始state改成和完整响应一致的结构,避免渲染时访问不存在的属性:
const [posts, setPosts] = useState({ data: [] });
2. 渲染时使用可选链判断
用可选链操作符?.防止访问undefined属性报错,即使初始state为空也能正常渲染:
return <div className="test">{posts.data?.[0]?.attributes?.Title}</div>;
3. 添加加载状态
在数据请求完成前显示加载提示,彻底避免渲染未就绪的数据:
import React, { useState, useEffect } from "react"; const App = () => { const [posts, setPosts] = useState({ data: [] }); const [loading, setLoading] = useState(true); useEffect(() => { const url = "http://localhost:1337/api/blogs"; const options = { headers: { Authorization: process.env.REACT_APP_STRAPI_TOKEN, }, }; const fetchData = async () => { try { const res = await fetch(url, options); const json = await res.json(); setPosts(json); setLoading(false); console.log(json); } catch (err) { console.log(err); setLoading(false); } }; fetchData(); }, []); if (loading) return <div>加载中...</div>; return <div className="test">{posts.data[0].attributes.Title}</div>; }; export default App;
内容的提问来源于stack exchange,提问作者Kyle
相关产品推荐
相关产品推荐

