本文已参与「新人创作礼」活动,一起开启掘金创作之路
HOOK的函数副作用
要理解什么是函数副作用,怎么清除函数副作用,我们首先要理解一个概念:什么是纯函数
纯函数通俗的来说就是每当有固定的输入,就会有固定的输出,当输入唯一时,输出也是唯一的
function add(x:number,y:number) : number { return x+y }
add(3,2) // 5
当函数add的入参固定时,返回结果也是固定的,是可以预见的
let number = 5
function sum(x:number,y:number): number {
return (x + y) * number
}
而在函数sum中,函数的返回结果依赖于函数外部的变量,即便入参唯一固定,函数结果也取决的外部变量number,所以这不是一个纯函数
let number = 0
function setNumber(x:number,y:number) : void {
if(x + y > 0) {
number = x + y
}else{
nmber = Math.abs(x + y)
}
}
在函数setNumber中,函数内部操作了函数外部的变量,这种操作就叫函数副作用
对应React中的函数副作用有发送请求,操纵dom,定时器等
useEffect应用详解:
在函数式组件中,我们使用useEffect代替 componentDidMount,componentDidUpdate 和 componentWillUnmount 来处理副作用操作。
对比useEffect和componentDidMount、componentDidUpdate和componentWillUnmount的执行时机;
在Layout阶段(即内存中的真实dom更新完成后),会异步调度useEffect中的effect,并加入到任务队列中,在组件被浏览器真正渲染到屏幕之后,才回去按顺序执行这些effect,如果需要再次执行某个effect时,会先执行这个useEffect中retuen的函数,即清除副作用,在组件卸载时,也会统一执行清除操作
useEffect的执行时机:
- 根据条件动态渲染时,无论是否有依赖项,都会执行useEffect,
- 初次渲染组件时,会按顺序执行所有useEffect,如果有依赖项,父组建没有传递依赖值时,依赖值取undefined
- 当没有依赖项时,只要组件更新就会执行useEffect,当依赖项为[]时,只有初次加载组件会执行,当有依赖项时,组件初次执行和依赖项改变才会执行
在useEffect中产生的函数副作用有很多种类,比如操作dom,记录日志,订阅外部数据源,发送请求,定时器等
而如操作dom,记录日志等通常是不需要清除操作的,而发送请求,定时器等操作容易造成数据泄露,所以很有必要进行清除操作
那么怎么中断useEffect中的异步数据请求呢
一般有两种方法
一是利用请求工具提供的强制中断方法
请求工具强制中断方法帮助文档:
axios github.com/axios/axios…
fetch developer.mozilla.org/en-US/docs/…
umi-request github.com/umijs/umi-r…
axios 取消请求
// axios 中断请求方法
useEffect(() => {
const source = axios.CancelToken.source();
const fetchData = async () => {
try {
const response = await Axios.get("/companies", {
cancelToken: source.token
});
// ...
} catch (error) {
if (Axios.isCancel(error)) {
//cancelled
} else {
throw error;
}
}
};
fetchData()
return () => {
source.cancel();
};
}, [companies]);
方法二是,让请求继续,等请求完成之后,判断组件是否已卸载,如果已卸载不进行后续操作
useEffect(() => {
let ignore = false
async function fetchData() {
const result = await axios(`https://hn.algolia.com/api/v1/search?query=${query}`);
if (!ignore) setData(result.data);
}
fetchData();
return () => { ignore = true }
},[])