React学习笔记:实用又好用的Hooks函数

2,117 阅读5分钟

本文正在参加「金石计划 . 瓜分6万现金大奖」

React框架以前是采用Class类编程,在类编程中使用生命周期比较方便,但是随着迭代更新,官方开始推荐使用函数式编程,但是函数式编程就没有状态这一个概念,于是乎官方就定义了一系列钩子函数来弥补在这一缺陷,那今天我们就来学习几个实用又好用的Hooks函数。

本文是基于个人的学习分享,可能不是特别全面,本文使用的是18版本的react,使用脚手架搭建的项目。

Hooks

动机:

  1. 解决组件的逻辑复用。
  2. 解决一些class类中存在的一些问题。

定义: Hook顾名思义就是钩子的意思,在函数组件中将React的class类组件中状态以及生命周期等这些特性引入到函数组件中,这就是React的Hook函数。所以在类组件中就是不能使用hook函数。

useState

之前的函数式组件,也被称之为无状态组件,只能接受父组件传过来的props做展示,函数式组件中没有state状态也没有生命周期,而 state Hook 可以让函数式组件使用状态。

useState是React中的一个Hook函数,它是一个方法,调用的时候传入一个默认值,返回的是一个数组,其中第一项是默认状态(默认值会赋予状态),数组第二项是一个用于更新设置默认值都的函数。

因为写数组下标去获取比较麻烦,所以采用了ES6中数组的结构,直接获取到一个状态值和修改状态值的函数。

让我们用一个例子来体会一下,其中的妙用,首先需要从react中引入useState

import { useState } from 'react';

function Demo() {
  const [count,setCount] = useState(0);
  const [name,setName] = useState('寒月十九');

  return (
    <div>
      <h3>{name}</h3>
      <button onClick={() => { setName('十九') }}>change</button>
      <h3>{count}</h3>
      <button onClick={() => { setCount(count + 1) }}>add</button>
    </div>
  );
}

useState.gif

所以useState支持多次调用,并且每一次调用返回的都是一个新的状态。

useEffect

在类组件中可以在componentDidMount组件首次渲染结束和componentDidUpdate组件更新中执行副作用,那在useEffect这个钩子函数中,通过传递的参数不同,实现不同的生命周期的效果。我们直接用实际效果直观感受一下。

首先useEffect接收两个参数,第一个参数是一个回调函数,第二个参数为依赖。

1.只传一个参数,并且里面没有返回值

import { useState } from 'react';

function Demo() {
  const [count,setCount] = useState(0);
  const [name,setName] = useState('寒月十九');
  useEffect(() => {
    console.log('副作用执行了!');
  })
  
  return (
    <div>
      <h3>{name}</h3>
      <button onClick={() => { setName('十九') }}>change</button>
      <h3>{count}</h3>
      <button onClick={() => { setCount(count + 1) }}>add</button>
    </div>
  );
}

useEffect1.gif

当组件渲染结束会执行一次,一加载有两次打印是因为解析jsx文件时为了解析为浏览器能读的懂得文件时,有一次组件的卸载,并再执行一次组建的渲染;当组件每次更新时都会触发这个钩子函数,相当于 ComponentDidUpdate 组件更新时。

2.只传一个参数,并且里面有返回值(返回出一个函数)

useEffect(() => {
   console.log('副作用执行了!');
    
   return () => {
      console.log('清除副作用!');
   }
 })

当useEffect传递一个参数并且第一个参数返回出一个函数,这个函数可以在组件卸载时清除副作用,也就是说相当于生命周期中的 componentWillUnmount 组件卸载时。

3.传递两个参数,第一个参数为回调,第二个参数为空数组时

import { useState } from 'react';

function Demo() {
  const [count,setCount] = useState(0);
  const [name,setName] = useState('寒月十九');
  useEffect(() => {
    console.log('副作用执行了!');
  },[])
  
  return (
    <div>
      <h3>{name}</h3>
      <button onClick={() => { setName('十九') }}>change</button>
      <h3>{count}</h3>
      <button onClick={() => { setCount(count + 1) }}>add</button>
    </div>
  );
}

useEffect2.gif

当第二个参数依赖为空数组时,useEffect钩子函数只会在组件渲染结束执行一次,那么就相当于ComponentDidMount 组件渲染结束时。通常我们会在这个钩子函数发接口请求。

4.传递两个参数,第一个参数为回调,第二个参数为基本类型时

import { useState } from 'react';

function Demo() {
  const [count,setCount] = useState(0);
  const [name,setName] = useState('寒月十九');
  useEffect(() => {
    console.log('副作用执行了!');
  },[count])
  
  return (
    <div>
      <h3>{name}</h3>
      <button onClick={() => { setName('十九') }}>change</button>
      <h3>{count}</h3>
      <button onClick={() => { setCount(count + 1) }}>add</button>
    </div>
  );
}

useEffect3.gif

当第二个参数设置的基本类型发生改变时才会执行这个钩子函数,而不是当组件发生更新就执行。

我们可以记住useEffect设个钩子函数比较强大,当设置的参数不同时,可以分别代替 ComponentDidMount 、 ComponentDidUpdate 、 ComponentWillUnmount 三个生命周期的使用。

当然这个钩子函数当然不止这么几种参数配置,由于是学习笔记,并不算全面。

useRef

useRef返回一个可变的ref对象,useRef接受一个参数绑定在返回的ref对象的current属性上,返回的ref对象在整个生命周期中保持不变。

import { useRef,useEffect } from 'react';

function Demo3() {

  const h1Ref = useRef(null);

  useEffect(() => {
    console.log(h1Ref);
  },[]);

  return (
    <div>
      <h1 ref={h1Ref}>this is h1</h1>
    </div>
  )
}

image.png

useContext

这个钩子函数主要用于爷孙组件传值,useContext Hook接受一个context对象(由createContext创建的对象)作为参数,并返回Context.Consumer。

import { createContext,useContext,useState } from 'react';

const Context = createContext();  // 创建一个上下文对象

function Bar() {
  const name = useContext(Context)
  return <div>Bar -- 从Demo4接收的参数:{name}</div>
}

function Foo() {
  return (
    <div>
      Foo
      <Bar />
    </div>
  )
}

function Demo4() {
  return (
    <div>
      <Context.Provider value={'寒月十九'}>
        <Foo />
      </Context.Provider>
    </div>
  )
}

Bar作为Foo的子组件,Foo又是Demo中的子组件,我们使用Context组件中的Provide方法给子组件传值,必须使用value传值。

image.png

如果我传递的值是Demo4中一个有响应式的值,那子组件取到的值也会是响应式的吗?

import { createContext,useContext,useState } from 'react';

const Context = createContext();  // 创建一个上下文对象

function Bar() {
  const name = useContext(Context)
  return <div>Bar --  从Demo4接收的参数:{name}</div>
}

function Foo() {
  return (
    <div>
      Foo
      <Bar />
    </div>
  )
}

function Demo4() {
  const [name,setName] = useState('寒月十九');
  return (
    <div>
      <Context.Provider value={name}>
        <Foo />
        <button onClick={() => setName('十九')}>changeName</button>
      </Context.Provider>
    </div>
  )
}

useContext.gif

可以看出,子组件接收到的参数也是响应式的。