useEffect概念理解与基础使用
在React中,useEffect是一个非常重要的Hook,它允许我们函数组件中执行副作用操作。副作用操作包括数据获取、手动 DOM 操作等。useEffect的使用有一些需要注意的地方,包括依赖项参数的设置、清理副作用等。下面是详细介绍如何在React中使用useEffect。
useEffect(setup, dependencies?)
useEffect需要函数需要传递两个参数,第一个参数是一个回调函数,第二个函数是一传入依赖项,该函数其实就是在不同的条件下帮我们执行我们传入回调函数,当依赖项为空数组的时候,在组件渲染完成的时候就会调用我们传入的回调函数(也称作副作用函数),下面将只先考虑依赖项为空的时候的useEffect的作用,请看下面的代码,在组件内部顶层调用useEffect。
useEffect(() => {
// 只执行一次
console.log('Component mounted');
}, []);
当传入的依赖项为空的时候,我们传入useEffect的副作用函数只会在组件渲染完成挂载的时候执行一次,在浏览器的控制台我们也可以看到信息的输出。
useEffect不同依赖项的说明
我们刚刚已经看过依赖项为数组的请款,还有另外两种情况,不传递依赖项,依赖项为变量,下面的表格说明了传入不同依赖项副作用函数的执行时机。
依赖项 | 副作用函数调用时机 |
---|---|
空数组 | 只在组件渲染挂载之后执行一次 |
不传递依赖项 | 在组件渲染挂载之后执行一次 + 组件更新渲染时执行 |
变量 | 组件渲染挂载之后执行一次,依赖项变量发生变化时执行 |
不传递依赖项
下面将演示不传递依赖项使用useEffect,如下面的代码所示,我们使用一个按钮控制子组件Son的显示,使用了不传递依赖项的useEffect函数,当我们点击toggle按钮的时候,Son组件将被重复的挂载和卸载。
import { useEffect, useState } from 'react'
const Son = () =>{
return <div>Son</div>
}
function App() {
const [show,setShow] = useState(true)
useEffect(()=>{
console.log('useEffect')
})
return (
<div>
{show && <Son></Son>}
<button onClick={()=>setShow(!show)}>Toggle</button>
</div>
);
}
export default App;
传递变量
下面将会传递一个count变量作为依赖项,我将会设置一个按钮,每当点击按钮时,count的值就会改变,同时也会执行副作用函数。
import { useEffect, useState } from 'react'
function App() {
const [count, setCount] = useState(0)
useEffect(()=>{
console.log('useEffect')
}, [count])
return (
<div>
<button onClick={()=>{setCount(count+1)}}>+{count}</button>
</div>
);
}
export default App;
可以看到在我们没有点击按钮时,useEffect就会先执行一次。
开始点击按钮之后,可以看到 useEffect被执行了多次。
清除副作用
当我们使用订阅或者计时器的时候,在组件卸载的时候需要卸载订阅或者清除定时器,可以在副作用函数的返回函数中进行清理操作,现在给出一个比较完整的useEffect的使用。
useEffect(() => {
// 这里是副作用操作
// 可选的清理函数
return () => {
// 在组件卸载或更新之前执行清理操作
};
}, []?); // 依赖数组
如果我们不进行副作用的清理,就会导致定时器或者订阅的一直存在,例如下面代码所示,在子组件上使用useEffect定义了一个定时器,在子组件渲染后就会执行该函数但是子组件卸载时定时器并没有消失。
import { useEffect, useState } from 'react'
const Son = () =>{
useEffect(()=>{
setInterval(()=>{
console.log('Son')
}, 1000)
},[])
return <div>Son</div>
}
function App() {
const [show, setShow] = useState(true)
return (
<div>
{show && <Son></Son>}
<button onClick={()=>setShow(!show)}>Toggle</button>
</div>
);
}
export default App;
如下图所示,当点击Toggle按钮后,子组件已被卸载,但是定时器并没有被清除。
在副作用函数当中加入返回函数,返回函数的内容将在组件卸载的时候执行,在这里清除定时器就可以完成清除副作用,如下面展示的代码所示。
useEffect(()=>{
const interval = setInterval(()=>{
console.log('Son')
}, 1000)
return ()=>{
clearInterval(interval)
}
})
这样在组件卸载以后,我们的定时器也就停止了。
封装自定义Hook
下面我将直接给出封装自定义Hook的一般思路:
- 使用use开头的函数命名
- 函数体内放入可复用的逻辑
- 把组件当中声明的状态变量以及函数返回给外层
- 在需要使用到该复用逻辑的地方调用该函数,执行获取到的变量以及函数、
下面是一个未经封装的控制组件显示的代码片段。
import { useState } from 'react'
const Son = () =>{
return <div>Son</div>
}
function App() {
const [show,setShow] = useState(true)
const toggle = () => {
setShow(!show)
}
return (
<div>
{show && <Son></Son>}
<button onClick={toggle}>Toggle</button>
</div>
);
}
export default App;
通过show变量和toggle函数我们很轻易就实现了控制组件显示的功能,很明显在很多其他地方我们也会需要控制组件的显示,那么这里就是可以复用的逻辑,按照之前给出的封装自定义Hook的步骤改进的代码如下所示。
import { useState } from 'react'
const Son = () =>{
return <div>Son</div>
}
const useShow = () => {
const [show, setShow] = useState(true)
const toggle = () => {
setShow(!show)
}
return [show, toggle]
}
function App() {
const [show, toggle] = useShow()
return (
<div>
{show && <Son></Son>}
<button onClick={toggle}>Toggle</button>
</div>
);
}
export default App;
这两我们就实现了自己自定义hook的封装。
React中Hook的使用规则
React的Hook函数只能在组件的顶层调用或者在其它Hook函数内调用。不在在for、if或者其它函数内调用。
下面就是一个错误的使用范例,在组件外部调用了useState。