【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排查速度。另外,不要忽略基础知识噢,温故而知新~

相关推荐
Csvn8 小时前
WASM
前端
修己xj9 小时前
我用 AI 给自己写了一个 Markdown 编辑器,然后把它开源了
前端
FRing0310 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
幸运小猿10 小时前
模拟慢上传-处理超时重试image 丢失线上问题
前端·javascript·html
moshuying12 小时前
网购能买Token了!
前端
h_654321012 小时前
echarts实现柱状图加一条横线
前端·echarts
风骏时光牛马12 小时前
生成一个后端标题,只需要一个标题,不需要其他的任何文本。
前端
子非鱼@Itfuture12 小时前
关于前后端联调中登录状态/登录信息失效?
java·前端
郭小李12 小时前
openFeign入门
java·开发语言·前端
馒头不想说话12 小时前
AI当运营,帮你省人力
前端