likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 9

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

一、bug现场

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

报错信息如下所示

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

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,在一个复杂组件中写了类似如下代码:

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。)

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

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

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

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

三、hooks使用规则

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

以下例子均来自react官网

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

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

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

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

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

四、小结

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

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