【react踩坑记】是时候重温最基本的hooks使用规则了

前期解决了一个项目中的bug,发现自己竟然在无意间违背了最基本的hooks使用规则,在此简单记录一下问题出现的原因并复习一下rules of hooks。

一、bug现场

在代码执行时,某些条件下程序报错。

报错信息如下所示

React has detected a change in the order of Hooks called by observerComponent. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: react.dev/warnings/in...

是的,hooks的使用出现了问题。

二、原因解析

通过观察开发者工具报错信息中提示的组件,发现我前段时间为修复另一个bug,在一个复杂组件中写了类似如下代码:

js 复制代码
function MyComponent({ cond }) {  
// hooks
const [list, setList] = useState()
const theme = useContext(ThemeContext);  
......
// 条件语句
if (cond) {  return; }  
......

// hooks
const initData = useMemo(()=>{
return ...;
},[])

......
// ...  

}

这违背了hooks基本的使用规则:Don't call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. (不要在循环、条件或嵌套函数中调用 Hooks。 在任何返回之前,要在顶层使用hooks。)

tips,为什么要制定这些规则呢?

在函数组件中调用Hook时,React会根据Hook的类型将其添加到当前组件的Hooks链表中。然后,React会将这些Hooks存储在Fiber节点的"memoizedState"字段中,以便在下一次渲染时使用。

react需要hooks在组件每次执行时的数量和顺序是一致的,从而实现正确地更新hooks链表,正确地将状态与组件关联,实现ui的正确更新。

想了解更多,可以参考juejin.cn/post/702081... 中对react源码的解析。

三、hooks使用规则

(1)不要在循环条件语句或者循环中调用hooks

以下例子均来自react官网

js 复制代码
function Bad1({ cond }) {  
if (cond) {  
// 🔴 Bad: inside a condition (to fix, move it outside!)  
const theme = useContext(ThemeContext);  
}  
// ...  
}  

function Bad2() {  
for (let i = 0; i < 10; i++) {  
// 🔴 Bad: inside a loop (to fix, move it outside!)  
const theme = useContext(ThemeContext);  
}  
// ...  
}

(2)不要在条件性return之后调用hooks

如解决上文的bug,把条件语句挪到所有hooks之后即可。

(3)不要在事件处理函数中调用hooks

js 复制代码
function Bad() {
function handleClick() {
// 🔴 Bad: inside an event handler (to fix, move it outside!)
const theme = useContext(ThemeContext);
}
// ...
}

(4)不要在类组件中调用hooks

js 复制代码
class Bad extends React.Component {  
render() {  
// 🔴 Bad: inside a class component (to fix, write a function component instead of a class!)  
useEffect(() => {})  
// ...  
}  
}

(5)不要在useMemo、useReducer、useEffect中调用hooks

js 复制代码
function Bad() {  
const style = useMemo(() => {  
// 🔴 Bad: inside useMemo (to fix, move it outside!)  
const theme = useContext(ThemeContext);  
return createStyle(theme);  
});  
// ...  
}

四、小结

当时我花了较多时间排查这个bug,发现自己违背了如此基础的hooks规则着实有点惭愧。不过吃一堑,长一智吧。

对于过于复杂的组件,可以拆分子组件,或者抽离自定义hooks,以降低复杂度,提升bug排查速度。另外,不要忽略基础知识噢,温故而知新~

相关推荐
mCell1 小时前
GSAP 入门指南
前端·javascript·动效
gnip2 小时前
组件循环引用依赖问题处理
前端·javascript
Aotman_3 小时前
el-input textarea 禁止输入中文字符,@input特殊字符实时替换,光标位置保持不变
前端·javascript·vue.js·前端框架·es6
Nan_Shu_6143 小时前
Web前端面试题(1)
前端·面试·职场和发展
EveryPossible3 小时前
选择数据展示
javascript
lypzcgf3 小时前
Coze源码分析-资源库-创建知识库-前端源码-核心组件
前端·typescript·react·coze·coze源码分析·ai应用平台·agent开发平台
百思可瑞教育4 小时前
在Vue项目中Axios发起请求时的小知识
前端·javascript·vue.js·北京百思教育
患得患失9494 小时前
【个人项目】【前端实用工具】OpenAPI to TypeScript 转换器
前端·javascript·typescript
大前端helloworld4 小时前
前端梳理体系从常问问题去完善-基础篇(html,css,js,ts)
前端·javascript·面试
trsoliu4 小时前
前端基于 TypeScript 使用 Mastra 来开发一个 AI 应用 / AI 代理(Agent)
前端·人工智能