副作用与数据请求

到目前为止,文章数据都写在代码里;真实博客的文章应当来自服务器。但 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 状态界面就不会白屏。但"每次输入都发请求"会过于频繁,下一章我们用清理函数实现防抖来优化它。

笔记加载中…