最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 扔掉你的Class吧,Hooks太香了(一)

    正文概述 掘金(小马子i)   2021-02-06   488

    简介

    Hook 是React16.8中新增的特性,它可以让你不编写class组件的情况下使用诸如state,生命周期等特性。

    动机

    在组件之间复用状态逻辑很难

    为了解决这类问题,也有出现过一些解决方案,如render props高阶组件Context等,它们都会导致组件树形成嵌套地狱(有点像回调函数和flutter的写法)。

    有了 Hook 之后,就可以从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使你在无需修改组件结构的情况下复用状态逻辑

    复杂组件变得难以理解

    随着业务扩张,组件内的逻辑状态和副作用也越来越多。我们在写 class 组件时,通常会将数据的获取放到compoentDidMountcomponentDidUpdate中,涉及到定时器或者数据订阅时,还需要在生命周期函数compoentWillUnmount去清除定时器或者取消数据订阅。这使得完全不相关的代码组合到一起,很容易产生bug,并且导致逻辑不清晰。

    为了解决这个问题,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分

    难以理解的 class

    在之前,如果你要使用诸如state生命周期等特性,就必须引入 class 组件,如果你要使用这些就必须花时间去理解 JavaScript 中this的工作方式。

    为了解决这些问题,Hook 使你在非 class 的情况下可以使用更多的 React 特性

    概览

    Hook 可以分为内置 Hook自定义 Hook

    内置 Hook 有:

    • useState
    • useEffect
    • useContext
    • useReducer
    • useCallback
    • useMome
    • useRef
    • useImperativeHandle
    • useLayoutEffect
    • useDebugValue

    自定义 Hook就是将上述内置 Hook进行组合,导出为一个以use开头的函数。


    这里先写一个基于class的 Counter组件,后面让我们来用Hook将它进行改造。

    import React from 'react'

    class Counter extends React.Component {
      constructor(props) {
        super(props)
        this.state = { count: 0 }
        this.setCount = this.setCount.bind(this)
      }

      setCount() {
        this.setState({
          count: this.state.count + 1
        })
      }
      
      render() {
        return (
          <>
            <h2>{ this.state.count }</h2>
            <button onClick={ this.setCount }>+</button>
          </>
        )
      }
    }

    useState

    让我们先来看一个最基础也同样重要的 Hook,现在我们开始改造上面的class组件。

    • 首先将class组件转为function组件,也就是函数式组件
    - class Counter extends React.Component {}

    + function Counter() {}
    • 接下来让我们将state等相关代码删掉,改用useStateHook,因为此处改动较多,直接上最终代码
    import React, { useState } from 'react'

    const Counter = () => {
      // 数组解构语法
      // 声明一个count变量,并声明一个setCount函数用于改变count的值
      const [count, setCount] = useState(0)
      
      return (
        <>
          <h2>{ count }</h2>
          <button onClick={ () => setCount(count + 1) }>+</button>
        </>
      )
    }

    乍一看代码简洁了很多,这难道不正是我们想要的嘛。(Hook 真香)

    如果我们有多个state变量,可以写成下面这种形式

    const [count, setCount] = useState(0)
    const [num, setNum] = useState(0)
    const [name, setName] = useState('')
    ...

    如果本次更新依赖于上一次的state值,可以这么写,此时setCount接收一个函数作为参数

    const [count, setCount] = useState(0)

    const handleClick = () => {
      setCount(prev => prev + 1)
    }

    <button onClick={ handleClick }>+</button>

    useEffect

    useEffect 可以让你在函数式组件执行一些副作用操作,它接收两个参数,第一个参数为必选的副作用函数,第二个为可选的参数(它是副作用函数执行的依赖项数组)

    还是刚才的例子,如果我们希望组件更新后执行一些副作用,比如打印当前count变量的值,又或者改变当前页面的 title

    // ...
    const [count, setCount] = useState(0)
    const [num, setNum] = useState(0)

    useEffect(() => {
      console.log(`当前count值,${count}`)
      document.title = `当前count值,${count}`
    })
    // ...

    上面的代码我们可以看到我们并没有添加第二个可选参数,这意味着,只要组件内状态发生改变(不仅仅是count,甚至num发生改变时),这个副作用函数都会执行。如果我们希望副作用函数只在 count 改变时候执行,这时候我们的第二个参数就派上用场了()

    // ...
    // 当前effect函数只依赖于 count 的改变,在 num 改变的时候并不会被执行
    // 相当于 class 组件中的 compoentDidMount 和 componentDidUpdate
    useEffect(() => {
      console.log(`当前count值,${count}`)
      document.title = `当前count值,${count}`
    }, [count])
    // ...

    如果我们仅仅希望副作用函数在组件挂载时候执行(compoentDidMount

    // useEffect 约定,在第二个参数传递空数组时,当前副作用函数只在组件挂载时被执行
    useEffect(() => {
      console.log(`当前count值,${count}`)
      document.title = `当前count值,${count}`
    }, [])

    有些时候我们可能会用到定时器,比如每秒钟更新一次时间

    const [time, setTime] = useState(new Date())

    const timer = null
    useEffect(() => {
      timer = setInterval(() => {
        setTime(new Date())
      }, 1000)
    })

    现在的代码有一个问题,想必大家都知道,那就是timer 没有被清除,我们在工作中会遇到很多这种场景,那么我们应该怎么去清除 timer 呢,答案是useEffect的第一个参数返回一个函数,这个被返回的函数会在组件被卸载时候自动被执行,可以清除诸如定时器之类的东西

    const [time, setTime] = useState(new Date())

    const timer = null
    useEffect(() => {
      timer = setInterval(() => {
        setTime(new Date())
      }, 1000)
      
      // 类似于 compoentWillUnMount
      
      // 当然这里也可以是一个具名函数
      return () => {
        clearInterval(timer)
        timer = null
      }
    })

    起源地下载网 » 扔掉你的Class吧,Hooks太香了(一)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元