🎧 Listen to this article: हिंदी · English · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
🌍 Read this in your language: हिंदी · தமிழ் · తెలుగు · ಕನ್ನಡ · മലയാളം · ଓଡ଼ିଆ · 日本語 · 中文
那个解决了一切问题的 Hook(但其实并没有)
当你第一次学习 React 时, useEffect 感觉很神奇。需要同步一些状态?有一个专门的 hook。想要获取数据?Hook。把两个 props 合并成一个?又一个 hook。看了一些教程之后,感觉就像是 useEffect 几乎是解决所有问题的答案。然后你构建了更大的项目。
今天是 2026 年 7 月 15 日,React 社区正在重新审视 useEffect。根据一位在大型应用程序上工作的高级开发者 Alejandro 最近的一篇文章,这个曾经感觉必不可少的模式,他现在使用的频率比他早年时减少了大约 80%。原因并不是说 useEffect 很糟糕——而是开发者用它解决的大多数问题都有更简单的解决方案。
这在当下很重要,因为越来越清楚的是,学好 React 意味着要学习 何时不 使用它最著名的工具。
useEffect 实际上是用来做什么的
让我们从官方说法开始。React 的文档将 effect 描述为“将你的组件与外部系统同步”的一种方式。关键词是: 外部。React 本身之外的事物——比如网络请求、WebSocket 连接、计时器、浏览器 API、订阅或第三方库。
如果你的 effect 没有与 React 之外的东西通信,很有可能你其实并不需要它。
你可能做错的五件事
在 effect 中派生值
最常见的模式之一是使用 useEffect 将 props 或状态组合成一个新值。例如:
const [fullName, setFullName] = useState("");
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
这可行,但 React 会渲染两次:一次使用原始的空状态,然后 effect 运行,状态改变,React 再次渲染。没有理由进行那次额外的渲染。
相反,只需在渲染期间计算该值:
const fullName = `${firstName} ${lastName}`;
更简单,更快,更少的渲染。
将 props 复制到状态中
另一个常见的模式是将 prop 同步到局部状态中:
const [user, setUser] = useState(props.user);
useEffect(() => {
setUser(props.user);
}, [props.user]);
这创建了两个单一事实来源。通常最后是一个被更新了,而另一个没有。除非你明确需要一个本地可编辑的副本(这很少见),否则直接使用 prop 即可。更简单的方法:
function Profile({ user }) {
return <h2>{user.name}</h2>;
}
单一事实来源。更容易调试。
在 effect 中过滤或转换列表
你可能见过这样的代码:在 effect 内部过滤列表,并将结果存储在状态中:
const [filteredUsers, setFilteredUsers] = useState([]);
useEffect(() => {
setFilteredUsers(users.filter(user => user.active));
}, [users]);
同样,这是不必要的状态。只需在渲染期间计算它:
const filteredUsers = users.filter(user => user.active);
如果计算成本很高,有一个专门的 hook——但它不是 useEffect。它是 useMemo,它会记忆结果,因此只有在依赖项更改时才会重新计算:
const filteredUsers = useMemo(() => {
return users.filter(user => user.active);
}, [users]);
但是请记住: useMemo 是一种 优化,而不是取代对代码的思考。
使用 effect 进行调试
有一个地方临时使用 effect 是有意义的:调试。每当值更改时记录日志确实很有用:
useEffect(() => {
console.log(user);
}, [user]);
但是这些应该在合并代码之前删除。
用旧方法获取数据
几年前,几乎每个 React 项目都有这种模式:
useEffect(() => {
fetch("/api/users")
.then(res => res.json())
.then(setUsers);
}, []);
这可行,但它做的并不多。没有错误处理,没有加载状态,没有重试逻辑,如果组件挂载两次也没有去重。大多数团队最终都自己构建了所有这些。
现在有了更好的选择。像 TanStack Query 和 SWR 这样的库会自动处理缓存、重试、后台重新获取、加载状态、错误状态和去重。你不需要编写 effect,而是使用一个 hook:
const { data, isLoading } = useQuery({
queryKey: ["users"],
queryFn: getUsers
});
代码少得多。错误少得多。更好的开发者体验。
当 Effect 隐藏真实问题时
随着时间的推移,Alejandro 注意到了一个模式:当一个组件有很多 effect 时,它通常做得太多了。也许它在一个组件内部获取数据、过滤、排序、格式化、验证并处理事件。这不是 effect 的问题——这是架构问题。
通常,将职责拆分到较小的 hook 或组件中会自动消除一半的 effect。
当你确实需要 useEffect 时
所有这些并不意味着“永远不要使用 useEffect。”有很多合理的理由:
WebSocket 连接: 你需要在组件挂载时打开连接,并在卸载时关闭连接。这正是 effect 的用途。
计时器: 如果你需要每 5 秒轮询一次更新, setInterval 在带有适当清理的 effect 内部是有意义的。
浏览器 API: 监听 window 的 resize 事件或与 localStorage 同步是副作用。
第三方库: 初始化图表库或分析 SDK——这些需要在组件挂载时运行。
这些正是 effect 被设计用来处理的场景:与 React 之外的事物同步。
改变一切的问题
在编写 effect 之前,Alejandro 会问自己一件事:“我是在与外部系统同步,还是在弥补我的组件设计的不足?”
他说,单单这个问题就从他的项目中去除了大量不必要的代码。
结论
useEffect 并不糟糕。它只是比大多数 React hook 更容易被滥用。现代 React 开发者倾向于使用派生值,保持 props 原样,使用查询库来处理服务器状态,并将 effect 留给那些确实需要它们的事情。结果是渲染次数更少,要管理的状态更少,错误更少,而且组件在几个月后更容易理解。
优点
- 减少不必要的重新渲染并提高性能
- 通过避免多余的状态和 effect 来简化代码
- 副作用较少的组件更容易调试和维护
- 查询库自动处理复杂的数据获取逻辑
- 当 effect 凸显设计问题时能带来更好的组件架构
- 状态越少意味着错误藏身的地方越少
缺点
- 需要学习何时使用替代方案(useMemo、自定义 hook、查询库)
- 习惯于重度 effect 模式的开发者可能需要改变他们的习惯
- 一些遗留项目严重依赖 useEffect 模式,无法在一夜之间重构
- 并非每个团队都采用了像 TanStack Query 这样的库
- 如果不谨慎处理,内联计算可能会降低可读性
注意事项
本文具有教育意义,并解释了社区文章中讨论的现代 React 模式。代码示例仅作说明之用——如果你在真实项目中使用它们,请将占位符值替换为你的实际 API 端点和逻辑。在将模式用于生产代码之前,始终对照原始出处验证它们。React 和生态系统发展迅速;请查看官方 React 文档和库文档(TanStack Query、SWR)以获取最新指南。
常见问题解答
-
什么时候应该使用 useEffect 而不是派生状态? ——仅当你与外部系统(API、计时器、浏览器事件)同步时,才使用
useEffect。如果你只是转换现有数据,请在渲染期间派生它。 -
useMemo 在计算方面比 useEffect 更好吗? —
useMemo能优化昂贵的计算,但要深思熟虑地使用它。大多数计算速度都足够快,可以在每次渲染时计算——仅在性能分析表明有影响时才进行记忆化。 -
我应该替换所有的 useEffect 数据获取吗? ——像 TanStack Query 这样的库比
useEffect强大得多,但迁移大型代码库需要时间。从新功能开始,然后逐步重构。 -
TanStack Query 和 SWR 之间有什么区别? ——两者都是处理缓存和重新获取的查询库。TanStack Query 功能更全面;SWR 更简单、更轻量。根据你项目的需求进行选择。
-
我还可以使用 useEffect 进行调试吗? ——可以,但是在合并代码之前删除调试 effect。使用浏览器的 DevTools 进行生产环境调试。
-
我如何知道我的组件是否做得太多? ——如果它有两个或三个以上的 effect,或者如果 effect 依赖于许多不同的东西,请考虑将其拆分为更小的组件或自定义 hook。
-
避免使用 useEffect 会让 React 变得更难学吗? ——并不真正如此——它意味着更好地学习 React 的核心模型。了解 何时不 使用某个功能通常可以澄清它的实际用途。
-
那 WebSockets 和浏览器 API 呢——它们总是需要 useEffect 吗? ——是的,如果你在管理 React 之外的某些事物的生命周期,带有适当清理的
useEffect就是正确的工具。
标签
#react #useeffect #javascript #webdev #frontend #reacthooks #modernreact #bestpractices
Incident Response: First Hour
A calm, evidence-preserving checklist for establishing control, bounding impact, communicating clearly, and containing an incident safely.
Free. No spam — unsubscribe in one click.


Responses
Sign in to leave a response.