useEffect:页面出现后再去做一件外部工作
一句话理解
组件负责描述页面。Effect 负责在页面显示之后,与组件外面的世界同步,例如请求后端、启动定时器或监听浏览器事件。
类比:你先打开一家店,再安排员工去仓库拿库存表。开店是显示组件,去仓库拿数据是 Effect。
具体场景:打开页面后读取留言
tsx
useEffect(() => {
loadMessages()
}, [])逐段读:
text
useEffect 告诉 React:页面显示后安排这项工作
() => { } 要执行的工作,这里调用 loadMessages
[] 依赖列表为空;这段代码不依赖某个会变化的值loadMessages 可能这样写:
tsx
async function loadMessages() {
const response = await fetch('/api/messages')
const data = await response.json()
setMessages(data)
}真实过程:页面先出现 → 浏览器发 GET 请求 → 后端查询数据库 → 返回 JSON → setMessages 更新列表 → 页面重新显示。
依赖数组到底是什么
tsx
useEffect(() => {
loadMessages(userId)
}, [userId])意思不是“把 userId 传给 Effect”。意思是:第一次显示后执行;以后 userId 变化时再执行。
为什么会不停请求
危险例子:
tsx
useEffect(() => {
loadMessages()
}, [messages])如果 loadMessages 最后调用 setMessages,流程会变成:
text
请求完成
→ messages 改变
→ Effect 因 messages 改变再次执行
→ 再请求
→ messages 又改变
→ 继续循环在 Network 面板看到同一个请求不断出现时,就检查 Effect 的依赖和更新关系。
为什么开发环境偶尔看到两次请求
React 的开发检查模式可能故意多运行一次某些逻辑,帮助发现没有正确清理的副作用。先确认生产构建是否仍然重复,再检查 Effect 是否可以安全执行两次。不要一看到两次就删除所有 Effect。
什么时候不该用 Effect
如果只是根据已有值计算另一个值,直接计算通常更清楚:
tsx
const visibleMessages = messages.filter((message) => message.visible)不需要先建另一个 state,再用 Effect 把它同步过去。
定时器要清理
tsx
useEffect(() => {
const timer = setInterval(refresh, 10000)
return () => clearInterval(timer)
}, [])返回的函数像“下班关灯”:组件离开页面时停止定时器。
自己试试
打开 DevTools 的 Network,刷新一次页面。找到最早出现的业务请求,再回到编辑器搜索它的 URL。确认它是否在 useEffect 调用的函数里,以及依赖数组里写了什么。
Network 里同一个请求不停出现,React 代码里首先检查哪里?
答案:检查发请求的 useEffect 依赖数组,以及请求完成后更新的 state 是否又触发了这个 Effect。