文章目录
useEffect
作用:在函数组件中使用生命周期函数
语法:具备多种情况
useEffect([callback], [dependencies])
用法
useEffect:在函数组件中,使用生命周期函数
useEffect(callback):没设置依赖
+ 第一次渲染完毕后,执行callback,等价于 componentDidMount
+ 在组件每一次更新完毕后,也会执行callback,等价于 componentDidUpdate
useEffect(callback,[]):设置了,但是无依赖
+ 只有第一次渲染完毕后,才会执行callback,每一次视图更新完毕后,callback不再执行
+ 类似于 componentDidMount
useEffect(callback,[依赖的状态(多个状态)]):
+ 第一次渲染完毕会执行callback
+ 当依赖的状态值(或者多个依赖状态中的一个)发生改变,也会触发callback执行
+ 但是依赖的状态如果没有变化,在组件更新的时候,callback是不会执行的
useEffect(()=>{
return ()=>{
// 返回的小函数,会在组件释放的时候执行
// 相当于componentWillUnmount
// 如果组件更新,会把上一次返回的小函数执行「可以“理解为”上一次渲染的组件释放了」
};
});
/*
返回的函数将在 组件卸载后 被调用
等同于 componentWillUnmount
*/
useEffect(() => {
return () => {
console.log('@4');
};
}, []);
/*
以及若干种排列组合
初次渲染和即将卸载时调用
等同于 componentWillUnmount
*/
useEffect(() => {
console.log('@5')
return () => {
console.log('@6');
};
}, []);
底层原理——effect链表
- 视图渲染开始时,遇到useEffect函数,会调用MountEffect方法将callback/依赖项加入到链表
- 视图渲染完毕时,基于UpdateEffect函数,监听时机执行链表中的effect callback
- 重新render时,首先销毁上一次的视图,因此会在上一次释放时首先调用componentWillUnmount时机的callback,即返回内容
- useEffect执行部分获取的是最新的状态值;(挂载时)返回的callback中获取的是卸载前的状态值
函数组件在渲染(或更新)期间,遇到useEffect操作,会基于MountEffect方法把callback(和依赖项)加入到effect链表
中!
在视图渲染完毕后,基于UpdateEffect方法,通知链表中的方法执行:
1、按照顺序执行期间,首先会检测依赖项的值是否有更新「有容器专门记录上一次依赖项的值」;有更新则把对应的callback执行,没有则继续处理下一项
2、遇到依赖项是空数组的,则只在第一次渲染完毕时,执行相应的callback
3、遇到没有设置依赖项的,则每一次渲染完毕时都执行相应的callback
import React from 'react';
import { useEffect, useState } from 'react';
import { Button } from 'antd';
export default function UseEffectDemo() {
let [num, setNum] = useState(0);
/*
在组件 第一次渲染完 && 每一次更新完 调用
等同于 componentDidMount && componentDidUpdate
*/
useEffect(() => {
// 获取最新的状态值
console.log('@1', num);
});
/*
只在组件 第一次渲染完 调用
等同于 componentDidMount
*/
useEffect(() => {
console.log('@2', num);
}, []);
/*
第一次渲染完 以及 依赖的状态发生改变 时调用
*/
useEffect(() => {
console.log('@3', num);
}, [num]);
/*
返回的函数将在 组件卸载后 被调用
等同于 componentWillUnmount
*/
useEffect(() => {
return () => {
// 获取的是上一次的状态值
console.log('@4', num);
};
});
/*
以及若干种排列组合
初次渲染和即将卸载时调用
等同于 componentWillUnmount
*/
useEffect(() => {
console.log('@5')
return () => {
console.log('@6');
};
}, []);
const handle = () => {
setNum(num + 1);
};
return <div className="demo">
<span className="num">{num}</span>
<Button type="primary"
size="small"
onClick={handle}>
新增
</Button>
</div>
}
只在函数最外层使用
只能在函数最外层调用 Hook,不要在循环、条件判断或者子函数中调用。
import React, { useState, useEffect } from "react";
export default function Demo() {
let [num, setNum] = useState(10);
if (num >= 10) {
// Error:React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render react-hooks/rules-of-hooks
useEffect(() => {
console.log('@1', num);
});
}
// 应该这样写
useEffect(() => {
if(num >= 10) console.log('@1', num);
});
return <div>
<span>{num}</span>
<button onClick={() => { setNum(num + 1); }}>处理</button>
</div>;
};
异步获取数据
问题:useEffect的callback不能用async修饰
不能直接对[callback]设置async,因为useEffect的callback只能返回一个函数(或者不设置返回值),而用async修饰函数之后,返回值总是Promise对象
无论async函数有无await操作,其总是返回一个Promise。
- 没有显式return,相当于return Promise.resolve(undefined);
- return非Promise的数据data,相当于return Promise.resolve(data);
- return Promise, 会得到Promise对象本身
import React, { useState, useEffect } from "react";
const queryData = () => {
return fetch('/api/subscriptions/recommended_collections')
.then(response => {
return response.json();
});
};
export default function Demo() {
let [data, setData] = useState([]);
/* // Warning: useEffect must not return anything besides a function, which is used for clean-up.
useEffect(async () => {
let result = await queryData();
setData(result);
console.log(result);
}, []); */
useEffect(() => {
const next = async () => {
let result = await queryData();
setData(result);
console.log(result);
};
next();
}, []);
return <div>
...
</div>;
};
解决方案一:不用async和await,使用Promise.then
useEffect(() => {
queryData()
.then(data => console.log(data))
}, [])
解决方案二:不用async修饰useEffect的callback,而是更深一层的函数,保证不会默认有Promise返回值
useEffect(() => {
const next = async () => {
let data = await queryData();
console.log(data);
};
next()
}, [])
注意事项
import React, { useState, useEffect } from "react";
export default function Demo() {
let [num, setNum] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
// ...
console.log(num); //不管如何更新都是0
}, 2000);
return () => {
clearTimeout(timer);
};
}, []);
return <div>
<span>{num}</span>
<button onClick={() => { setNum(num + 1); }}>处理</button>
</div>;
};