为什么需要useEffect
了解useEffect之前得了解React中的两种逻辑处理方式,就能知道为什么需要useEffcect了
- 渲染组件:可能是由传递过来的props,对这些props进行转化,变为jsx中显示的组件,渲染组件部分的代码必须是纯粹的,传递什么参数,就会得到唯一样式的组件。而不会做其他任何事情
- 事件处理程序:组件内部的除了渲染组件代码部分的事件处理函数,这些函数可能会代替渲染组件部分的代码来执行副作用:例如按钮点击而引起的处理函数(副作用)
但还有一种场景下这两种并不能胜任,例如当组件挂载的时候而引起的副作用,而不是由特定事件而引起的副作用。这时候useEffect就能很好的处理这部分的逻辑
useEffect允许你指定由渲染自身,而不是特定事件而引起的副作用
例如在一个ChatRoom组件当中
const serverUrl = 'https://localhost:1234';
function ChatRoom({ roomId }) {
const [count,setCount]=useState(0);
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);
const handleCountChange=()=>{
setCount(count+1);
}
return {
<>
<div>ChatRoom ${roomID}</div>
<div>count:${count}</div>
<button onclick={handleCountChange}count++<button/>
</>
}
}渲染组件的代码部分:const [count,setCount]=useState(0);给定了状态,组件根据这个State和传递过来的props来渲染此组件
<>
<div>ChatRoom ${roomID}</div>
<div>count:${count}</div>
<button onclick={handleCountChange}count++<button/>
</>根据props渲染出来的组件是唯一的,就是纯函数,也就是我们说的React逻辑处理的第一种方式
<ChatRoom props={roomId=1}/>//都是一模一样的,因为是纯函数
<ChatRoom props={roomId=1}/>
<ChatRoom props={roomId=1}/>在其中handleCountChange这个事件处理函数就是我之前说的第二种逻辑处理方式,即由特定事件引发的副作用(即这个handleCountChange函数)。
而这些特定事件没有包括渲染自身等等事件,所以就由useEffect出手啦
在自身组件渲染时,就会触发useEffect(),其中()里面有两个参数,一个传递的箭头函数,一个是数组,箭头函数中执行的这部分是核心代码
const connection = createConnection(serverUrl, roomId);
connection.connect();即当组件挂载时,客户端与聊天室建立连接(副作用),你不可能在这里添加一个button,说点击一下再和聊天室建立连接吧(事件处理程序),这样体验太糟糕了。
深入解剖useEffect的结构
我们还是用ChatRoom的例子
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();
return () => {
connection.disconnect();
};
}, [roomId]);可以看出useEffect有两个参数,一个箭头函数,一个数组,其中这个数组的是什么作用呢?实际上,这个数据是一个依赖项,可以控制useEffect的执行时机:
useEffect(() => {
// 这里的代码会在每次渲染后运行
});
useEffect(() => {
// 这里的代码只会在组件挂载(首次出现)时运行
}, []);
useEffect(() => {
// 这里的代码不但会在组件挂载时运行,而且当 a 或 b 的值自上次渲染后发生变化后也会运行
}, [a, b]);再来看一下剪头函数内部
useEffect(() => {
const connection = createConnection(serverUrl, roomId);
connection.connect();//被useEffect包裹的副作用
//这个return返回的是一个清理函数
return () => {
connection.disconnect();
};
}, [roomId]);里面除了被包裹的核心副作用代码之外,有一个return语句,返回的是一个函数,这个函数的作用是 清理上一次渲染组件时的副作用。但执行时期并不是想象中卸载组件的那时候,而是当重新渲染组件的时候(注意,这里不是说的卸载组件),会执行这个函数,用于清理上一次渲染时产生的副作用,并且执行新的useEffect中的核心代码
例如组件生命周期对应的组件的行为
3. ChatRoom 组件挂载,roomId 设置为 "general"
4. ChatRoom 组件更新,roomId 设置为 "travel"
5. ChatRoom 组件更新,roomId 设置为 "music"
6. ChatRoom 组件卸载
在组件生命周期的每个阶段,Effect 执行了不同的操作:
7. Effect 连接到了 "general" 聊天室
8. Effect 断开了与 "general" 聊天室的连接,并连接到了 "travel" 聊天室
9. Effect 断开了与 "travel" 聊天室的连接,并连接到了 "music" 聊天室
10. Effect 断开了与 "music" 聊天室的连接