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

相关推荐
zzzsde2 分钟前
【C++】深入理解string类(5)
java·前端·算法
袁煦丞6 分钟前
随机菜谱解救选择困难!YunYouJun/cook 成为你的厨房锦囊:cpolar内网穿透实验室第549个成功挑战
前端·程序员·远程工作
携欢13 分钟前
PortSwigger靶场之CSRF where token is tied to non-session cookie通关秘籍
运维·服务器·前端
我是华为OD~HR~栗栗呀26 分钟前
华为OD-21届考研-Java面经
java·前端·c++·python·华为od·华为·面试
詩句☾⋆᭄南笙26 分钟前
CSS美化网页元素
前端·css·html
程序00727 分钟前
HTML+JS+CSS实现汽车官网
javascript·css·html
陈随易27 分钟前
不使用 Husky 和 Lint-staged,实现 Git 提交前自动格式化代码
前端·后端·程序员
范特东南西北风28 分钟前
roocode + vscode + api_key = free GPT-5
前端·ai编程
golang学习记29 分钟前
从0死磕全栈之Next.js 缓存与数据重新验证
前端
BraveAriesZyc30 分钟前
axios的使用
前端