使用 Hooks

# 使用 Hooks

# useEffect

初学 useEffect 时,我们难免习惯于借助对生命周期的理解来推导对 useEffect 的理解。但长期来看,若是执着于这个学习路径,无疑将阻碍你真正从心智模式的层面拥抱 React-Hooks。

有时候,我们必须学会忘记旧的知识,才能够更好地拥抱新的知识。对于每一个学习 useEffect 的人来说,生命周期到 useEffect 之间的转换关系都不是最重要的,最重要的是在脑海中构建一个“组件有副作用 → 引入 useEffect”这样的条件反射——当你真正抛却类组件带给你的刻板印象、拥抱函数式编程之后,想必你会更加认同“useEffect 是用于为函数组件引入副作用的钩子”这个定义。

useEffect 快速上手 useEffect 可以接收两个参数,分别是回调函数与依赖数组,如下面代码所示:

useEffect(callBack, [])

useEffect 用什么姿势来调用,本质上取决于你想用它来达成什么样的效果。下面我们就以效果为线索,简单介绍 useEffect 的调用规则。

每一次渲染后都执行的副作用:传入回调函数,不传依赖数组。调用形式如下所示:

useEffect(callBack)

仅在挂载阶段执行一次的副作用:传入回调函数,且这个函数的返回值不是一个函数,同时传入一个空数组。调用形式如下所示:

useEffect(()=>{
  // 这里是业务逻辑 
}, [])

仅在挂载阶段和卸载阶段执行的副作用:传入回调函数,且这个函数的返回值是一个函数,同时传入一个空数组。假如回调函数本身记为 A, 返回的函数记为 B,那么将在挂载阶段执行 A,卸载阶段执行 B。调用形式如下所示:

useEffect(()=>{
  // 这里是 A 的业务逻辑

  // 返回一个函数记为 B
  return ()=>{
  }
}, [])

这里需要注意,这种调用方式之所以会在卸载阶段去触发 B 函数的逻辑,是由 useEffect 的执行规则决定的:useEffect 回调中返回的函数被称为“清除函数”,当 React 识别到清除函数时,会在卸载时执行清除函数内部的逻辑。这个规律不会受第二个参数或者其他因素的影响,只要你在 useEffect 回调中返回了一个函数,它就会被作为清除函数来处理。

每一次渲染都触发,且卸载阶段也会被触发的副作用:传入回调函数,且这个函数的返回值是一个函数,同时不传第二个参数。如下所示:

useEffect(()=>{
  // 这里是 A 的业务逻辑

  // 返回一个函数记为 B
  return ()=>{
  }
})

上面这段代码就会使得 React 在每一次渲染都去触发 A 逻辑,并且在卸载阶段去触发 B 逻辑。

其实你只要记住,如果你有一段副作用逻辑需要在卸载阶段执行,那么把它写进 useEffect 回调的返回函数(上面示例中的 B 函数)里就行了。也可以认为,这个 B 函数的角色定位就类似于生命周期里 componentWillUnmount 方法里的逻辑(虽然并不推荐你再继续钻生命周期的牛角尖,哈哈)。

根据一定的依赖条件来触发的副作用:传入回调函数(若返回值是一个函数,仍然仅影响卸载阶段对副作用的处理,此处不再赘述),同时传入一个非空的数组,如下所示:

useEffect(()=>{
  // 这是回调函数的业务逻辑 

  // 若 xxx 是一个函数,则 xxx 会在组件卸载时被触发
  return xxx
}, [num1, num2, num3])

这里我给出的一个示意数组是 [num1, num2, num3]。首先需要说明,数组中的变量一般都是来源于组件本身的数据(props 或者 state)。若数组不为空,那么 React 就会在新的一次渲染后去对比前后两次的渲染,查看数组内是否有变量发生了更新(只要有一个数组元素变了,就会被认为更新发生了),并在有更新的前提下去触发 useEffect 中定义的副作用逻辑。

# HooWhy React-Hooks

梳理了以下 4 条答题思路:

  • 告别难以理解的 Class;这个说法的背后是 this 和生命周期这两个痛点。函数组件没this是如何绑定state的也就是说我们怎么知道这个state是这个组件的,如果这个state不是这个组件的,那么是不是可以更好的复用组件

  • 解决业务逻辑难以拆分的问题;

  • 使状态逻辑复用变得简单可行;

  • 函数组件从设计思想上来看,更加契合 React 的理念