乐趣区

关于前端:hooks-系列三useEffect

快来退出咱们吧!

“ 小和山的菜鸟们 ”,为前端开发者提供技术相干资讯以及系列根底文章。为更好的用户体验,请您移至咱们官网小和山的菜鸟们 (https://xhs-rookies.com/) 进行学习,及时获取最新文章。

“Code tailor”,如果您对咱们文章感兴趣、或是想提一些倡议,微信关注 “小和山的菜鸟们” 公众号,与咱们取的分割,您也能够在微信上观看咱们的文章。每一个倡议或是同意都是对咱们极大的激励!

前言

这篇文章,咱们次要目标是理解一下 共享状态钩子(useEffect) 的应用.

useEffect

定义

Effect Hook 能够让你在函数组件中执行副作用操作,换句话说能够实现一些相似 Class 中生命周期的性能。

如何应用

useEffect() 的应用如上面语句。

useEffect(() => {dosomeing(); // 执行一些副作用操作
},[]);

useEffect 须要参数

  • 第一个参数:该 Hook 接管一个蕴含命令式、且可能有副作用代码的函数。
  • 第二个参数:该 useEffect 在哪些 state 发生变化时,才从新执行;

useEffect 做了什么

通过 useEffectHook,能够通知 React 须要在渲染后执行某些操作;而 useEffect 的第一个参数要求咱们传递一个函数,在 React 执行完更新 DOM 操作之后,就会执行这个函数,默认状况下,无论是组件第一次渲染之后,还是组件更新之后,都会执行这个函数,咱们能够在这个要传递的函数中执行一些相似 Class 组件中生命周期要做的工作,比方:扭转 DOM、增加订阅、设置定时器、记录日志等。 要晓得,在没有这个 useEffect Hook 之前在函数组件内的这些副作用操作是不被容许的。

提醒

如果你相熟 React class 的生命周期函数,你能够把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。

革除 effect

Class 组件中,某些副作用代码,咱们须要在 componentWillUnmount 中革除,比方咱们的创立计时器与革除计时器,同样的,在函数组件卸载时也须要革除 effect 创立的诸如订阅或计时器 ID 等资源。要实现这一点,useEffect 函数需返回一个革除函数。以下就是一个创立计时器的例子:

const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)

useEffect(() => {const interval = setInterval(() => {
    // 创立计时器
    addCount((val) => val + 1)
  }, timer)
  return () => {
    // 返回革除函数
    clearInterval(interval) // 革除计时器
  }
})

为避免内存透露,革除函数会在组件卸载前执行。另外,如果组件屡次渲染(通常如此),则 在执行下一个 effect 之前,上一个 effect 就已被革除。在上述示例中,意味着组件的每一次更新都会创立新的计时器。若想防止每次更新都触发 effect 的执行,这就须要用到 useEffect 的第二个参数。

effect 的条件执行

默认状况下,effect 会在每轮组件渲染实现后执行。这样的话,一旦 effect 的依赖发生变化,它就会被从新创立。

然而,在某些场景下这么做可能会矫枉过正。比方,下面计时器的例子中,咱们并不需要在每次组件更新时都创立新的计时器,而是仅须要在 source prop 扭转时从新创立。

这里就要用到 useEffect 承受的第二个参数,它是 effect 所依赖的值数组。更新后的示例如下:

const [count, addCount] = useState(0)
const [timer, addTimer] = useState(1000)

useEffect(() => {const interval = setInterval(() => {
    // 创立定时器
    addCount((val) => val + 1)
  }, timer)
  return () => {clearInterval(interval) // 革除定时器
  }
}, [count])

当传入第二参数的时候,在这里也就是当 count 扭转后才会从新创立计时器,也就是说,useEffect 会依据咱们传入的第二参数,来决定本次更新是否执行。

留神

  • 如果你要应用此优化形式,请确保数组中蕴含了 所有内部作用域中会发生变化且在 effect 中应用的变量,否则你的代码会援用到先前渲染中的旧变量。
  • 如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),能够传递一个空数组([])作为第二个参数。

比照类组件

置信你当初曾经对 useEffect 有所理解了,那咱们用一个简略的例子,来感受一下在函数组件中应用 HookClass 组件的区别。

有一个简略的计时器,默认以 1000ms 的工夫距离计数,点击加按钮后,计时工夫距离减少,计时器计数变慢。

类组件实现

首先咱们要在类组件中申明一个变量 count 来计数,一个变量 timer 来管制计数工夫距离,每隔 timer 个工夫,count 加一,而后增加一个按钮,每次点击时,timer 减少 1000ms。同时 count 减少速度变慢。

class Example extends React.Component {constructor(props) {super(props)
    this.state = {
      count: 0,
      timer: 1000,
    }
  }

  componentDidMount() {this.interval = setInterval(() => {
      // 创立定时器
      this.setState({count: this.state.count + 1,})
    }, this.state.timer)
  }
  componentDidUpdate() {clearInterval(this.interval) // 先革除上一个定时器
    this.interval = setInterval(() => {
      // 创立以 更新后的 timer 计时距离定时器
      this.setState({count: this.state.count + 1,})
    }, this.state.timer)
  }
  componentWillUnmount() {clearInterval(this.interval) // 卸载时革除定时器
  }

  render() {
    return (
      <div>
        <p> 以后 count 值为:{this.state.count}</p>
        <p> 以后计数工夫距离为:{this.state.timer}ms</p>
        <button onClick={() => this.setState({ timer: this.state.timer + 1000})}>Click me</button>
      </div>
    )
  }
}

咱们能够发现,在这个 class 中,咱们须要在两个生命周期函数中编写反复的代码。还要在组件卸载时革除计时器,大量的反复代码,显得十分繁琐,那么在函数组件中应用 Effect Hook 会是怎么的呢?

应用 Effect Hook 实现

import React, {useState, useEffect} from 'react'

function Example() {const [count, addCount] = useState(0)
  const [timer, addTimer] = useState(1000)

  useEffect(() => {const interval = setInterval(() => {addCount((val) => val + 1)
    }, timer)
    return () => {clearInterval(interval)
    }
  }, [count])

  return (
    <div>
      <p> 以后 count 的值为:{count}</p>
      <p> 以后计数工夫距离为: {timer}ms</p>
      <button onClick={() => addTimer((val) => val + 1000)}>Click me</button>
    </div>
  )
}

看了这个例子之后,你是不是很惊奇的发现,比照 Class 组件的 constructor 函数没有了,写在三个生命周期中的函数在函数组件被 useEffect 一个家伙解决了!是不是看起来很精简。置信你有上面几个问题:

  • useEffect 做了什么?

    通过应用这个 Hook,你能够通知 React 组件须要在渲染后执行某些操作。React 会保留你传递的函数(咱们将它称之为“effect”),并且在执行 DOM 更新之后调用它。

  • 为什么在组件外部调用 useEffect

    useEffect 放在组件外部让咱们能够在 effect 中间接拜访 count state 变量(或其余 props)。

  • useEffect 会在每次渲染后都执行吗?

    是的,默认状况下,它在 第一次渲染之后 每次更新之后 都会执行(你能够利用后面提到的第二个参数来管制渲染条件)。而且,React 保障了每次运行 effect 的同时,DOM 都曾经更新结束。

小结

  • useEffect Hook 实际上为咱们带来了,在函数组件中也能解决副作用的性能,他好比componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。
  • useEffect HookClass 函数的生命周期不同的一点:useEffect 在 DOM 都曾经更新结束才执行,componentDidMount 则是在组件挂在后(插入 DOM 树中)立刻调用,componentDidUpdate() 会在更新后会被立刻调用。首次渲染不会执行此办法。componentWillUnmount() 会在组件卸载及销毁之前间接调用。
  • useEffect Hook 的呈现之前,组件开发中逐步会被状态逻辑和副作用充斥。每个生命周期经常蕴含一些不相干的逻辑。而它的呈现帮忙咱们更好的拆分这些状态逻辑,并非强制依照生命周期划分,为开发削减了许多的可能。

下节预报

在下节中,咱们将为大家介绍 useRefs,敬请期待!

退出移动版