React进阶 会使用Hook吗?

紫棋的一句想要我帮你唱hook吗?让hook红了一把!
作为一个开发,尤为是前端开发,在电视上听到这个词仍是有点小兴奋的(虽然彼hook非此hook)。玩够这个梗,是否是也要了解一下本身的react hook?html

clipboard.png

1、why hook

使用react有一段时间了,组件化编程也早已成为习惯。经常使用的两种编写组件的方式就是就是class组件函数组件
class组件:经过继承React.Component来构建组件,虽然提供了state状态和完备的生命周期函数,可是也有不少不方便的地方。前端

  • 不少事件须要在挂载期componentDidMount更新期componentDidUpdate重复书写。有些反作用还须要在卸载期componentWillUnmount卸载。代码重复度很高,并且难以理解,一旦忘记了就会引发很多bug。
componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
    ChatAPI.subscribeToFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(
      this.props.friend.id,
      this.handleStatusChange
    );
  }
  • 经过class书写天然会碰到 this 的问题,要想使用this函数不能忘记绑定。代码十分冗余。

函数组件:经过函数直接书写的组件,看起来是简洁了许多,可是不能使用state,也没有生命周期函数、更不能使用react的一些其余特性。只能经过父组件传递进来,任人鱼肉,只能惨淡的沦为展现组件react

那么,函数组件就只能沦为展现的花瓶吗?能结合class组件能使用react特性的优势和函数组件简洁优雅的特性吗?好在react hook来了,函数组件的春天来了。编程

React 16.8 新增Hook特性。它可让你在不编写 class 的状况下使用 state 以及其余的 React 特性。api

2、 what's hook

是否是感受hook挺神奇的?hook是什么?怎么使用?不用惊讶,hook就是一个钩子函数,钩着react的一些api供给函数组件使用。数组

先看一个官方的useState例子:函数

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 “count” 的 state 变量。
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

引入一个hook(useState函数),传入一个初始值0,而后函数返回了一个数组,经过数组的结构赋值,取得state, count = 0和修改count值的函数setCount。当button发生点击事件时,触发setCount函数,而且传入新的count值完成对state值的修改,并展现到页面上。代码转换成class组件以下:组件化

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

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

看明白了吗?性能

Hook概念

Hook 是一些可让你在函数组件里“钩入” React state生命周期等(context、refs)特性的函数。
React 内置了一些像 useState 这样的 Hook。你也能够建立你本身的 Hook 来复用不一样组件之间的状态逻辑。优化

hook使用规则

一个组件中可使用多个hook,每一个hook会独立存在,内部状态不会共用。每次更新组件时,hook会按顺序从上到下执行。

function Form() {
  // 1. Use the name state variable
  const [name, setName] = useState('Mary');

  // 2. Use the age state variable
  const [age, setAge] = useState('12');


  // 3. Use the surname state variable
  const [surname, setSurname] = useState('Poppins');

  // ...
}
第一次执行结果:
设置状态name Mary;
设置状态age 12;
设置状态surname Poppins;

第二次执行结果:
设置状态name Mary;
设置状态age 12;
设置状态surname Poppins;

那么,react是如何知道哪一个 state 对应 哪个useState?很简单, React 靠的是 Hook 调用的顺序。

react hook 使用,要遵循下面两个规则:

  • 只在最顶层使用 Hook
    react依靠hook调用顺序来对应state,因此调用顺序不能变。

    function Form() {
      // 1. Use the name state variable
      const [name, setName] = useState('Mary');
    
      // 2. Use the age state variable
      if(!name){
          const [age, setAge] = useState('12');
       }
    
      // 3. Use the surname state variable
      const [surname, setSurname] = useState('Poppins');
    
      // ...
    }
    第一次执行结果:
    设置状态name Mary;
    设置状态age 12;
    设置状态surname Poppins;
    
    第二次执行结果:
    设置状态name Mary;
    设置状态age 12;//判断后会被忽略
    设置状态surname 12;//此时设置为12,与需求已不符

    也就是说只能在顶层使用hook。

  • 只在 React 函数中调用 Hook

    在class组件中是没法使用hook函数的,只能在函数组件中使用。
    在自定义hook中也可使用。

内置Hook

上面使用的useState Hook是一种可让你在函数组件内使用state的内置hook,除此以外react还提供了许多内置hook:
基础 Hook

useState
useEffect
useContext

额外的 Hook

useReducer
useCallback
useMemo
useRef
useImperativeHandle
useLayoutEffect
useDebugValue

下面看一下 useState、useEffect两个经常使用的hook,其余的使用频率较低,使用方法也都基本一致其余hook

3、state Hook

咱们想在函数组件里加入state,点击+,完成数字累加功能:

import React , {useState} from 'react' 


function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    return <>
        {`${count.name}已经${count.age}岁了`}
        <div onClick={()=>{setCount({name:'janny',age:count.age+1})}}>+</div>
    </>
}

export default Item;

从上面代码能够看出来,useState hook提供了在函数组件里使用state的能力。十分简洁。
首先,经过react引入useState钩子函数;
接着,在函数组件内调用useState,并传入state的初始值,能够是个值,也能够是对象。useState函数的返回值个数组,这里经过数组的结构赋值取得count(变量名能够随意定义)和修改count的方法setCount(函数名也能够随意定义)。
取值state:能够经过变量count直接取值
修改state:经过给setCount函数传递值来修改

是否是很简单?之后终于能够在函数组件里使用state了,拥有控制本身主权的能力了!除了使用state,还有一种场景也是咱们常常碰到的,那就是函数组件没有生命周期,一些反作用的操做无法完成!别着急,react又内置了极好的effect hook!

4、effect Hook

当咱们点击+时,不只想完成数字累加,并且想要在title里显示咱们的修改,若是在class组件里,咱们能够在componentDidMount生命周期里经过setState完成,可是函数组件里无法完成,即便如今有useState Hook也没有生命周期函数,无法完成!那就在来一个hook吧,引入effect Hook。

useEffect hook不只提供了生命周期,并且useEffect Hook能够看作componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。也就是在这一个钩子函数里能够完成不少事!

1.无需清除的反作用

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

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
       document.title = count.age
    })
    return <>
        {`${count.name}已经${count.age}岁了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

export default Item;

好好感觉一下!useEffect就像是一个生命周期函数。这个功能在 class 组件中,须要在两个生命周期函数(componentDidMount、componentDidUpdate)中编写重复的代码。在函数useEffect内进行反作用操做就好了。由于每次挂载、更新后useEffect都会执行。

在 React 组件中有两种常见反作用操做须要清除的和不须要清除的。上面这种就是不须要清除的反作用。

2.须要清除的反作用

假如,咱们以为只用手指点击太累了,想弄个计时器帮咱们执行。

let timer = null;

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer);//清除
        timer = setInterval(()=>{
            console.log(1)
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age
    })
    return <>
        {`${count.name}已经${count.age}岁了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

这样就很棒,换上自动挡就很舒服!可是当咱们切换路由,移除这个组件后,出去看看风景回来!发现定时器没有停,本身玩的很嗨...哎呦喂!

clipboard.png

这就是须要清除的反作用,class组件里咱们能够经过在componentWillUnmount生命周期函数里清除,因此这里咱们也要清除这些须要清除的反作用!

function Item(){
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer)
        timer = setInterval(()=>{
            console.log(1)
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age;
       return function(){//add
        clearInterval(timer)
       }
    })
    return <>
        {`${count.name}已经${count.age}岁了`}
        <div onClick={()=>{setCount({name:'tom',age:count.age+1})}}>+</div>
    </>
}

useEffect清除反作用的方式也很简单,return 一个函数,在函数里清除就能够了。

3.优化 useEffect

上面说了useEffect hook在每次挂载、更新后都会执行。这不就是跟setState在性能方面形成的问题同样严重吗?class组件 经过在 componentDidUpdate 进行优化,useEffect怎么优化!

能够经过useEffect函数的第二个参数进行优化。

1.传递空数组,表示函数只执行一次!这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,因此它永远都不须要重复执行。effect 内部的 props 和 state 就会一直拥有其初始值。更接近你们熟悉的 componentDidMount 。

useEffect(()=>{
 ...
},[])

2.传递包含state的数组

会经过===比较count的前一次渲染的值这一次要渲染的值,若是全等则React 会跳过这个 effect,这就实现了性能的优化。必定要将全部须要比较的state都放进数组,不然将不会进行比较,也就会直接跳过

useEffect(()=>{
 ...
},[count])

5、自定义Hook

react内置了多hook,咱们也能够对这些hook进行业务方面的封装,写出属于本身的hook!咱们将上面的叠加计时封装成一个自定义hook:数字每秒叠加,而且修改显示在title上。

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

function useTitle(){//自定义hook
    let timer = null;
    const [count,setCount] = useState({name:'tom',age:11})
    useEffect(()=>{
        clearInterval(timer)
        timer = setInterval(()=>{
            setCount({name:'tom',age:count.age+1})
        },1000)
       document.title = count.age;
       return function(){
        clearInterval(timer)
       }
    },[count])
    
    return count;//返回一个对象
}


function Item(){//自定义hook使用
   const count = useTitle()
    return <>
        {`${count.name}已经${count.age}岁了`}
    </>
}

将一些公共代码提取出来,最后返回须要在页面显示的数据就成了

6、总结

到这里,react hook已经有所了解了吧!就是让函数组件可以像class组件同样自由自在的使用react的特性的函数。能够不影响以前任何业务,在项目中彻底可选100%向后兼容的react新特性!快在项目中用起来吧!

若有不妥!欢迎指正
相关文章
相关标签/搜索