同一个 React Bug,我分别丢给了 DeepSeek、豆包和 GPT,差距比想象中大
现在很多前端开发遇到 Bug,第一反应已经不是先去 Stack Overflow,也不是翻半天官方文档,而是直接把代码丢给 AI。
但问题也随之出现了:
明明都是 AI,为什么有时候一个模型能直接指出问题,另一个模型却会给你改一大堆代码,最后 Bug 还在?
这次就拿一个 React 开发里非常常见、而且特别容易被 AI “带跑偏”的问题,做一次同题模拟对比。
为了避免把不同版本、不同时间的模型表现写死,下面主要比较 DeepSeek、豆包和 GPT 在这类问题中的典型分析路径和解决思路,不把它当成绝对排名。
一、先看这个 React Bug
假设页面里有这样一段代码:
import React, { useEffect, useState } from "react";
export default function UserList() {
const [users, setUsers] = useState([]);
const [keyword, setKeyword] = useState("");
const params = {
keyword,
page: 1
};
useEffect(() => {
fetch("/api/users?" + new URLSearchParams(params))
.then(res => res.json())
.then(data => {
setUsers(data.list);
});
}, [params]);
return (
<div>
<input
value={keyword}
onChange={e => setKeyword(e.target.value)}
placeholder="搜索用户"
/>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}
乍一看,好像没什么问题。
逻辑也非常简单:
用户输入关键词 → keyword 改变 → 请求接口 → 更新列表。
但实际运行以后,你可能会发现一个很离谱的现象:
接口一直请求。
哪怕用户什么都没操作,Network 里也可能连续出现请求。
如果接口响应比较快,甚至有机会直接把后端接口刷爆。
二、问题到底出在哪?
真正的问题其实就在这里:
const params = {
keyword,
page: 1
};
以及:
useEffect(() => {
// ...
}, [params]);
很多刚开始写 React 的开发者第一眼并不会意识到问题。
因为从人的角度看:
{
keyword: "",
page: 1
}
和下一次渲染得到的:
{
keyword: "",
page: 1
}
内容明明完全一样。
但 React 在判断依赖是否变化的时候,并不会帮你做对象内容的深度比较。
每次组件重新渲染时:
const params = {
keyword,
page: 1
};
都会创建一个新的对象引用。
也就是说:
{} !== {}
所以 React 会认为:
params 又变了。
于是整个过程变成:
组件渲染
↓
创建新的 params
↓
useEffect 发现 params 变化
↓
请求接口
↓
setUsers
↓
组件重新渲染
↓
又创建新的 params
↓
useEffect 再执行
↓
继续请求
这才是真正的根因。
三、把这个问题交给不同 AI,会发生什么?
这里才是我觉得比较有意思的地方。
因为这种 Bug 本身并不难。
真正能拉开体验差距的,往往不是:
AI 知不知道 useEffect。
而是:
AI 能不能迅速判断“哪一行代码才是真正的根因”。
四、DeepSeek:答案通常比较直接
如果把整段代码和“为什么接口一直请求”这个问题交给 DeepSeek,比较容易得到类似这样的分析:
params是一个对象,每次组件重新渲染都会创建新的引用,而useEffect依赖了params,因此 React 会认为依赖发生变化,导致 Effect 重复执行。
然后一般会给出几个解决方案。
例如直接修改依赖:
useEffect(() => {
const params = {
keyword,
page: 1
};
fetch("/api/users?" + new URLSearchParams(params))
.then(res => res.json())
.then(data => {
setUsers(data.list);
});
}, [keyword]);
这个答案其实已经能解决问题了。
对于这种单文件、问题明确、上下文比较少的 Bug,DeepSeek 的表现通常已经完全够用。
而且它的优势很明显:
中文解释比较自然。
对于国内开发者来说,直接把报错、代码和需求一起扔进去,使用门槛很低。
五、豆包:解释会更偏“教学型”
豆包面对这种问题时,一个比较明显的特点是:
它往往会把问题拆得更细。
比如不仅告诉你:
params 每次渲染都会重新创建。
还可能顺便解释:
- 什么叫引用类型
- useEffect 怎么比较依赖
- 为什么数组和对象容易触发类似问题
useMemo是干什么的- 为什么不能随便把对象放进依赖数组
例如可能给出这样的改法:
const params = useMemo(() => {
return {
keyword,
page: 1
};
}, [keyword]);
然后:
useEffect(() => {
fetch("/api/users?" + new URLSearchParams(params))
.then(res => res.json())
.then(data => setUsers(data.list));
}, [params]);
这同样是正确方案。
对于刚接触 React 的开发者来说,这种回答其实挺舒服。
因为它不是只告诉你:
“改这里。”
而是会解释:
“为什么要改这里。”
六、GPT 的区别,往往不是“知道答案”
再看 GPT。
如果只是问:
为什么这个 useEffect 会无限请求?
其实三个模型都有很大概率答出来。
所以真正值得看的,并不是这一道题谁会做。
而是继续往下追问:
这个项目实际开发中应该怎么改?
这时候答案开始出现差异。
比较成熟的分析通常不会急着告诉你:
useMemo
而是先判断:
这个 params 有必要存在吗?
因为从当前代码看:
const params = {
keyword,
page: 1
};
只是为了发请求。
那最简单的方案其实不是增加一个 useMemo。
而是直接把真正影响请求的状态作为依赖:
useEffect(() => {
const searchParams = new URLSearchParams({
keyword,
page: "1"
});
fetch(`/api/users?${searchParams}`)
.then(res => res.json())
.then(data => {
setUsers(data.list);
});
}, [keyword]);
反而更简单。
这也是我觉得 AI 编程里一个很容易被忽略的问题:
能修好 Bug,不等于修改方案就是最合理的。
七、为什么“疯狂 useMemo”不一定是好答案?
看到对象引用变化以后,很多人会下意识写:
const params = useMemo(() => ({
keyword,
page: 1
}), [keyword]);
技术上当然没问题。
但如果整个项目里一碰到对象就 useMemo,代码很快就会变成:
const params = useMemo(...)
const options = useMemo(...)
const config = useMemo(...)
const filter = useMemo(...)
最后组件里到处都是缓存。
这其实属于:
为了 Hooks 而 Hooks。
真正应该考虑的是:
这个对象是不是一定需要成为组件渲染阶段的变量?
如果只是某一个 Effect 内部使用,很多时候直接放进 Effect 里反而更加清晰。
这类“代码能不能继续简化”的判断,才是我认为现在不同 AI 编程体验真正容易拉开差距的地方。
八、再继续追问一步:其实还有隐藏问题
如果这是一段真实业务代码,我还会继续问 AI:
用户快速输入关键词怎么办?
比如用户连续输入:
a
ab
abc
abcd
那就可能瞬间发送四次请求。
甚至还可能出现:
abcd 的请求先回来
abc 的请求后回来
结果旧数据把新数据覆盖了。
这时候问题已经不再是一个简单的:
useEffect 无限循环。
而变成:
- 是否需要防抖
- 是否需要取消旧请求
- 如何避免竞态
- loading 状态怎么处理
- 请求失败怎么处理
- 组件卸载以后是否还需要更新状态
真正用于生产环境的代码,可能会继续演进成:
useEffect(() => {
const controller = new AbortController();
const timer = setTimeout(async () => {
try {
const searchParams = new URLSearchParams({
keyword,
page: "1"
});
const response = await fetch(
`/api/users?${searchParams}`,
{
signal: controller.signal
}
);
const data = await response.json();
setUsers(data.list);
} catch (error) {
if (error.name !== "AbortError") {
console.error(error);
}
}
}, 300);
return () => {
clearTimeout(timer);
controller.abort();
};
}, [keyword]);
你会发现:
最开始明明只是一个:
“为什么接口一直请求?”
最后其实可以延伸出完整的前端请求设计问题。
九、这才是我现在判断 AI 编程能力的方法
以前大家喜欢问:
哪个 AI 写代码最强?
但我现在越来越觉得,这个问题本身就太宽泛了。
因为如果只是:
写一个冒泡排序
或者:
写一个 React TodoList
现在很多模型都能完成。
真正应该测试的是:
第一,能不能找到根因
不是看到报错就开始改代码。
第二,能不能减少无意义修改
一个 Bug 最怕 AI 一次改十几个文件。
最后:
原来的问题没解决,又创造三个新问题。
第三,能不能理解真实项目环境
Demo 和真实业务完全是两回事。
真实项目还有:
- 状态管理
- 权限
- 接口封装
- TypeScript
- ESLint
- 路由
- 请求缓存
- 历史代码
- 团队规范
第四,能不能继续往下发现问题
优秀的 AI 不应该只回答:
“这一行错了。”
而应该能进一步提醒你:
即使修改这一行,当前请求逻辑还有竞态风险。
这种能力在真正开发项目时,价值要高很多。
十、DeepSeek、豆包和 GPT,到底怎么选?
如果只是这一个 React Bug,我觉得完全没必要神化任何模型。
DeepSeek 能解决。
豆包也能解决。
GPT 同样能解决。
真正的区别,往往会随着任务复杂度增加才逐渐出现。
如果你的使用场景只是:
- 看报错
- 写 SQL
- 查 API
- 写一个函数
- 解释代码
国内 AI 对很多开发者来说已经非常够用了。
但如果开始进入:
- 大段代码分析
- 连续 Debug
- 多轮修改
- 复杂业务逻辑
- 项目级重构
- 多文件上下文
- 长时间协作开发
这时候才更值得认真比较不同模型。
我自己比较建议的是:不要一上来就纠结“谁最强”,而是把自己平时最真实的代码丢进去跑几天。
尤其是一直在用 DeepSeek、豆包,但还没有认真体验过 GPT 的开发者,实际拿同一个项目对比一次,感受会比看各种模型跑分直观得多。
如果后面确实发现 GPT 更适合自己的工作流,再考虑是否需要开通 Plus。国内支付不方便的,也可以自行了解一下 gpt211·com 这类 AI 订阅服务平台,不过第三方渠道还是建议先看清楚开通方式、售后规则以及是否需要提供账号密码,再决定是否使用。
这类东西没必要跟风。
能不能真正提高自己的开发效率,才是最重要的。
最后
这次这个 React Bug 本身其实很简单。
真正让我觉得有意思的,反而是一个变化:
以前程序员遇到 Bug,会搜索“这个问题怎么解决”;现在越来越多的人开始思考“这个问题应该交给哪个 AI 解决”。
而下一阶段,真正拉开效率差距的可能也不是:
你有没有使用 AI。
而是:
你能不能让 AI 真正理解你的项目,而不是只会让它生成几段看起来能运行的代码。
下一篇我准备继续拿一个更接近真实项目的问题测试:
“同一个 Spring Boot 接口响应变慢的问题,DeepSeek、豆包和 GPT 谁能真正定位到原因?”
更多推荐

所有评论(0)