HOOK的函数副作用

72 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

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代替 componentDidMountcomponentDidUpdatecomponentWillUnmount 来处理副作用操作。

对比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 }
},[])