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

相关推荐
Jmyd01236 小时前
Web端3D营销展厅技术实现路径:从素材上云到线索回流的数据链路
前端·3d·性能优化·三维数字化
半个落月6 小时前
LangChain Agent 中间件实战(上):生命周期、状态、短路与工具包
javascript·langchain
砚凝霜6 小时前
【软考信息安全】第二十二章 Web服务器安全分析与增强——Apache与IIS
服务器·前端·安全
半个落月6 小时前
LangChain Agent 中间件实战(下):DeepAgents文件权限、长期记忆与对话摘要
javascript·langchain
u0111026758 小时前
JPG、PNG、WebP 怎么选?按透明度、体积和使用场景做判断
前端·javascript·vue.js·图像处理·ai作画
jerrywus9 小时前
让 AI 写完代码后交一份报告:我改造了 answer-me-with-html
前端·claude·cursor
派小心.9 小时前
小程序分享链路中来源参数的传递校验
大数据·前端·数据库·人工智能·小程序·数据分析
hhb_6189 小时前
一键生成数据看板,ECharts实战演示
前端·javascript·echarts
Java后端的Ai之路10 小时前
08_React组件API
前端·react.js·前端框架
小马不起床10 小时前
零基础玩转 FastAPI_09:前端基础工程构建
前端·状态模式·fastapi