开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第18天,点击查看活动详情
前言
这两天学习了React新版本所带来的Hook特性,也是感觉到它所带来的便利,但在学习过程中还是踩了一些坑,下面就来记录一下。
React中Hook可能踩的坑
hook定义规则
官方有两条Hook定义规则:
- 只在最顶层使用 Hook
- 只在 React 函数中调用 Hook
第一条规则即不要在if-else等条件控制语句中调用Hook,这会导致Hook的执行顺序出现问题,从而导致BUG。如果我们想要根据条件执行Hook,就将条件控制语句放置Hook中。
// 🔴 在条件语句中使用 Hook 违反第一条规则
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
useEffect(function persistForm() {
// 👍 将条件判断放置在 effect 中
if (name !== '') {
localStorage.setItem('formData', name);
}
});
第二条规则则是因为普通函数本身并不能保存状态本身,例如下面的例子代码:如果有开启eslint编译的话,就会抛出一个错误。
import React, {useEffect} from 'react';
import ReactDOM from 'react-dom/client';
function normalFunction() {
useEffect( () => {
console.log( "Hello" );
} )
return (<div>subDom</div>)
}
function Demo() {
useEffect( () => {
console.log( "只执行一次" );
}, [] )
return (
<>
<div>
DEMO
</div>
{normalFunction()}
</>
);
}
let root = ReactDOM.createRoot(
document.getElementById( "root" )
);
root.render( <Demo/> )
// React Hook "useEffect" is called in function "normalFunction" that is neither a React function component nor a custom React Hook function.
如果我们强制执行,这个普通函数所定义的Hook都会挂载到其调用的父组件去,在如上的例子就是会由Demo组件来挂载。
useEffect重复调用
有时会发现useEffect
一直在执行,这可能是因为在函数中更改了state从而导致dom刷新,一直触发useEffect(),这时我们就需要定义对应的deps
参数了。
import React, {useEffect, useState} from 'react';
import ReactDOM from 'react-dom/client';
function Demo() {
const [str, setStr] = useState( "default" )
useEffect( () => {
console.log( "useEffect被执行" ); // 不断被执行
setStr( Math.random().toString() )
} )
return (
<div>
{str}
</div>
);
}
let root = ReactDOM.createRoot(
document.getElementById( "root" )
);
root.render( <Demo/> )
useEffect未调用
有时我们定义了useEffect
对应的依赖项deps
,却发现更改state
后,useEffect
却没有被调用。可能是因为React是通过浅比较来判断值是否发生了变化,才决定是否调用useEffect
。例如我们定义一个对象数组,只修改其中一个对象的属性,这时React是无法感知的。