副作用与数据请求
到目前为止,文章数据都写在代码里;真实博客的文章应当来自服务器。但 React 组件的渲染本身是"纯"的:它只根据 props/state 计算界面。像请求数据这种发生在渲染之外的操作,叫做副作用(side effect),由 useEffect 负责。本章让博客挂载后自动请求文章列表。
useEffect 长什么样
useEffect 接收两个参数:要执行的函数 + 依赖数组。函数里的代码会在渲染完成后运行:
import { useEffect } from "react";
useEffect(() => {
console.log("组件挂载完成,可以发请求了");
}, []); // 空数组:只在首次挂载后执行一次
用 fetch 请求列表
组件挂载后请求 /api/posts,把结果写入 state。同时用 loading 状态记录请求进度,界面据此切换"加载中 / 数据"两种形态:
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/posts")
.then((res) => res.json())
.then((data) => {
setPosts(data);
setLoading(false);
})
.catch(() => setLoading(false));
}, []); // 只请求一次
if (loading) return <p>加载中…</p>;
return <ArticleList posts={posts} />; // ArticleList 是之前拆好的组件
开发期没有后端怎么办
本地没有接口时,先写一个"延迟返回的 Promise"模拟请求,等后端就绪后再换回 fetch:
// src/api/posts.js —— 模拟接口
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function fetchPosts() {
await sleep(500); // 模拟 500ms 网络延迟
return mockPosts; // 返回假数据,结构与真实接口保持一致
}
组件里的写法不变:fetchPosts().then(setPosts),以后只改这一个函数就能接上真实接口。
依赖数组控制执行时机
依赖数组决定 effect 何时重新执行:数组中的变量发生变化时执行一次;不写数组则每次渲染都执行,很容易引发死循环,一般不要这样写。看一个例子:
const [keyword, setKeyword] = useState("");
useEffect(() => {
console.log("keyword 变成了:" + keyword); // keyword 一变就执行
}, [keyword]); // 依赖 keyword
小结
useEffect 配合空依赖数组 = "挂载后请求一次",是数据请求的标准姿势,再配上 loading 状态界面就不会白屏。但"每次输入都发请求"会过于频繁,下一章我们用清理函数实现防抖来优化它。