写代码出错了,很少深入了解 hook。让ai给总结了一下。
这确实是 React 中非常核心且容易被忽视的"铁律"。React 的很多设计理念与传统的命令式编程(如 jQuery)或基于类的编程(Vue Options API)不太一样,因此有很多类似的"潜规则"和陷阱。
以下为您整理了 React 开发中必须知道的 6 个核心注意事项,掌握这些可以帮您避开 90% 的 Bug:
1. useEffect 依赖数组不能"撒谎" (Stale Closure / 闭包陷阱)
这是 Hook 最容易出 Bug 的地方。如果你在 useEffect 或 useCallback 内部使用了某个变量,但没有把它加到依赖数组 [] 中,Hook 内部就会一直引用旧的变量值。
-
❌ 错误写法:
JavaScriptconst [count, setCount] = useState(0); useEffect(() => { const timer = setInterval(() => { console.log(count); // 永远打印 0!因为这里的 count 是闭包捕获的初始值 }, 1000); return () => clearInterval(timer); }, []); // 依赖数组是空的,导致 effect 只运行一次,内部永远拿着旧的 count -
✅ 正确写法:
JavaScriptuseEffect(() => { const timer = setInterval(() => { console.log(count); }, 1000); return () => clearInterval(timer); }, [count]); // 依赖 count,每次 count 变化,effect 会销毁旧的并重新运行或者使用函数式更新(如果不依赖外部变量触发 Effect):
JavaScriptsetCount(prevCount => prevCount + 1); // 这种写法不需要依赖 count
2. State 更新是"异步"的(快照特性)
调用 setState 后,状态不会立即改变。React 会把更新放入队列,等到下一次渲染时才会生效。
-
❌ 常见误区:
JavaScriptconst handleClick = () => { setCount(count + 1); console.log(count); // 打印的仍然是旧值!不是 +1 后的值 // 如果此时发请求,发出去的也是旧值 fetchData(count); }; -
✅ 正确理解:
如果你需要使用更新后的值,应该使用 useEffect 监听该值的变化,或者在 setState 中使用回调函数(仅用于计算新值)。
JavaScriptuseEffect(() => { console.log(count); // 这里才能拿到更新后的值 fetchData(count); }, [count]);
3. 永远不要直接修改 State (Immutability / 不可变性)
React 比较状态是否变化是基于引用比较(Shallow Compare)。如果你直接修改对象属性,引用地址没变,React 就不知道数据变了,页面就不会刷新。
-
❌ 错误写法:
JavaScriptconst [user, setUser] = useState({ name: 'Alice', age: 18 }); const updateName = () => { user.name = 'Bob'; // 修改了内容,但对象引用没变 setUser(user); // React 认为前后是同一个对象,不触发重新渲染 }; -
✅ 正确写法:
JavaScriptconst updateName = () => { // 创建一个新对象(复制旧属性 + 覆盖新属性) setUser({ ...user, name: 'Bob' }); };
4. 列表渲染必须有唯一的 key(且尽量不要用 index)
key 是 React 识别 DOM 节点的身份证。如果使用数组索引(index)作为 key,当列表顺序发生变化(插入、删除、排序)时,React 会复用错误的 DOM 节点,导致状态错乱。
-
❌ 风险写法:
JavaScript// 如果列表顺序会变,千万别用 index {items.map((item, index) => <li key={index}>{item.name}</li>)} -
✅ 正确写法:
JavaScript// 使用数据中的唯一 ID {items.map((item) => <li key={item.id}>{item.name}</li>)}
5. useEffect 的清理函数 (Cleanup Function)
如果在 useEffect 中订阅了事件、设置了定时器或发起了请求,必须返回一个清理函数,否则会导致内存泄漏或逻辑错误(比如组件卸载了但定时器还在跑)。
-
✅ 标准模板:
JavaScriptuseEffect(() => { // 1. 设置副作用 const handleResize = () => console.log(window.innerWidth); window.addEventListener('resize', handleResize); // 2. 返回清理函数 return () => { window.removeEventListener('resize', handleResize); }; }, []);
6. React Strict Mode 下的"重复执行"
在开发环境(Development)中,为了帮你检查不纯的副作用,React 的 <StrictMode> 会故意将你的组件渲染两次 ,useEffect 也会执行两次。
- 现象 :你看控制台日志,发现 API 请求发了两次,或者
console.log打印了两次。 - 注意 :这不是 Bug,生产环境(Production)不会发生。不要为了解决这个问题去写奇怪的代码,而应该确保你的 Effect 是可以重复执行且无害的(做好清理工作)。
总结建议
您刚遇到的 "Hook 顺序与数量必须一致" 是 React 运行机制的物理限制(React 内部是靠数组索引来记录每个 Hook 的状态的,如果顺序乱了,状态就张冠李戴了)。
而上面提到的这 6 点,更多是逻辑和模式上的注意事项。掌握了这些,您写 React 就会非常有底气了。