0


React Hooks(钩子函数)

React Hooks

什么是Hooks?

首先:React的组件创建方式,一种是类组件,一种是纯函数组件

React团队认为组件的最佳写法应该是函数,而不是类。

但是纯函数组件有着类组件不具备的特点:

  • 纯函数组件没有状态
  • 纯函数组件没有生命周期
  • 纯函数组件没有this

这就注定,纯函数组件只能做UI展示的功能,如果涉及到状态的管理与切换,我们就必须得用类组件或者redux,但是在简单的页面中使用类组件或者redux会使代码显得很重。

因此,React团队设计了React hooks(钩子)。

React Hooks的意思是:组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码“钩”进来。

四种常用的钩子:

  • useState()
  • useReducer()
  • useContext()
  • useEffect()
  • useRef()

UseState()

我们知道,纯函数组件没有状态,useState()用于为函数组件引入状态。

举例:
state是一个普通变量:

//引入状态钩子useState()import React,{useState}from'react'import'./App.css';functionApp(){//useState创造一个状态,赋值一个初始值,当前赋值的初始值为0//count是一个变量,此变量的值指向当前状态的值 相当于this.state//setcount是一个函数,此函数可以修改状态的值 相当于this.Setstateconst[count,setCount]=useState(0)constaddCount=()=>{setCount(count+1)}return(<div className="App"><div>{count}</div><button onClick ={addCount}>点击加1</button></div>);}exportdefault App;

在这里插入图片描述

state是一个对象:

setState()不会局部更新

意思是,如果state是一个对象,不能部分setState,所以我们使用

…user

将原来的内容复制过来,再加上要修改的内容,相当于将前面的内容覆盖。

import React,{useState}from'react'import'./App.css'functionApp(){const[user,setUser]=useState({age:'11',name:'Bob'})consthanderClick=()=>{setUser({...user,name:'jack'})}return(<div className='App'><h1>{user.name}</h1><h1>{user.age}</h1><button onClick={handerClick}>
            Click
          </button></div>)}exportdefault App;

改变前:
在这里插入图片描述
改变后:
在这里插入图片描述

useReducer()

useState() 的替代方案,用于包含多种状态,或者下一个 state 依赖于之前的 state,实现函数组件的状态管理。

基本原理是通过用户在页面中发起action, 从而通过reducer方法来改变state, 从而实现页面和状态的通信。

举例:

点击加1,点击减1

//实现点击改变状态import React,{useReducer}from'react'import'./App.css';functionApp(){//useReducer(),state表示状态,action表示相关操作constreducer=(state,action)=>{if(action.type ==='add'){return{...state,count: state.count +1,}}elseif(action.type ==='jian'){return{...state,count: state.count -1,}}else{return state
    }}constaddCount=()=>{dispatch({type:'add'})}constmin=()=>{dispatch({type:'jian'})}const[state,dispatch]=useReducer(reducer,{count:0})return(<div><div>{state.count}</div><button onClick={addCount}>点击加1</button><button onClick={min}>点击减1</button></div>)}exportdefault App;

在这里插入图片描述

useContext()

useContext()用于在组件之间共享状态,而不必显式地通过组件树的逐层传递 props。

实现步骤:

  1. 使用createContext创建Context对象
  2. 在顶层组件通过provider提供数据
  3. 在底层组件通过useContext函数获取数据
//引入状态钩子useState()import React,{useContext}from'react'import'./App.css';functionApp(){//通过createContext来创建上下文const AppContext = React.createContext()constAchild=()=>{//在子组件中通过useContext来获取数据const{name1}=useContext(AppContext)return(<div>
                这是组件A,使用的name值是:{name1}</div>)}constBchild=()=>{//在子组件中通过useContext(Context句柄)来获取数据const{name2}=useContext(AppContext)return(<div>
                这是组件B,使用的name值是:{name2}</div>)}return(//AppContext.Provider数据共享组件,来确定共享范围,通过value来分发内容<AppContext.Provider value={{name1:'jack',name2:'Bob'}}><Achild></Achild><Bchild></Bchild></AppContext.Provider>);}exportdefault App;

在这里插入图片描述

useEffect()

useEffect()可以检测数据更新 。,可以用来更好的处理副作用,比如异步请求等。

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出Effect()的依赖项。

只要数组发生改变,useEffect()就会执行。

当第二项省略不填时,useEffect()会在每次组件渲染时执行,这一点类似于componentDidMount。

useEffect回调在dom渲染完毕之后执行 和vue里边的Watch效果比较像,但是执行时机是不同的 watch一开始就执行了

举例:

第二个参数省略时:

import React,{useState,useEffect}from'react'import'./App.css';functionApp(){const[loading,setLoading]=useState(true)//相当于componentDidMount//useEffect()第二个参数未填useEffect(()=>{setTimeout(()=>{setLoading(false)},3000)})//loadling为true时显示Loading... 3秒后loading变成了false,显示内容加载完毕return(
    loading?<div>Loading</div>:<div>内容加载完毕</div>)}exportdefault App;

第二个参数存在时:
name改变时会触发useEffect()函数,

import React,{useState,useEffect}from'react'import'./App.css';functionAsyncPage({name}){const[loading,setLoading]=useState()const[person,setPerson]=useState({})//useEffect()函数在组件初始化执行一次,之后,name改变时才会执行//组件渲染时,两秒后从Loading变为Bob//name改变时,先从Bob变为Loading,两秒后变为指定名字useEffect(()=>{setTimeout(()=>{setLoading(false)setPerson({name})},2000)},[name])return(
      loading?<div>Loading...</div>:<div>{person.name}</div>)}functionApp(){const[name,setName]=useState('Bob')constchangeName=(name)=>{setName(name)}return(<div><AsyncPage name ={name}/><button onClick ={()=>changeName('Jack')}>将名字改为jack</button><button onClick ={()=>changeName('Tom')}>将名字改为Tom</button></div>)}exportdefault App;

useEffect()返回一个函数:

当useEffect()返回一个函数时,该函数会在组件卸载时执行。

举例:

当点击switch时,组件被卸载,定时器被清除,控制台不再打印。

import React,{useEffect,useState}from'react'import'./App.css';functionTest(){useEffect(()=>{let timer =setInterval(()=>{
      console.log('定时器正在执行')},1000)return()=>{//清除定时器clearInterval(timer)}},[])return(<div>this is test</div>)}functionApp(){const[flag,setFlag]=useState(true)return(<div>{flag?<Test/>:null}<button onClick={()=>{setFlag(false)}}>switch</button></div>)}exportdefault App;

useRef()

用于在函数组件中获取真实的DOM元素对象或者是组件实例。(因为函数组件没有实例,所以这里的获取组件实例指的是获取类组件实例)

使用步骤:

  1. 导入useRef()函数
  2. 执行useRef()函数并传入null,返回值为一个对象,内部有一个current属性存放拿到的dom对象(组件实例)
  3. 通过ref绑定要获取的元素或者组件实例。

举例:

获取dom和组件实例,可以看到结果在控制台打印了出来

import React,{useEffect, useRef}from'react'import'./App.css';//组件实例 类组件(函数组件没有实例)//dom对象 标签classTestextendsReact.Component{render(){return(<div>我是类组件</div>)}}functionApp(){const testRef =useRef(null)const h1Ref =useRef(null)//useEffect回调在dom渲染完毕之后执行//和vue里边的Watch效果比较像,但是执行时机是不同的 watch一开始就执行了useEffect(()=>{
    console.log(testRef.current)
    console.log(h1Ref.current)},[])return(<div>{/* 获取类组件实例 */}<Test ref={testRef}/>{/* 获取DOM对象 */}<h1 ref={h1Ref}>this is h1</h1></div>)}exportdefault App;

自定义钩子函数

根据自己的业务需求,自行封装一个钩子函数以供自己使用。

举例:自定义一个获取表单数据的钩子函数

import React,{useState}from'react'import'./App.css';// 自定义hook(use开头)// 重用受控表单创建state和onChange方法逻辑/**
 * 
 * @param {string | number} initialValue 初始默认值
 * @returns 
 *///获取表单数据constuseInput=(initialValue)=>{const[value, setValue]=useState(initialValue)return{
    value,onChange:e=>setValue(e.target.value)}}// 表单组件constApp=()=>{const username =useInput('admin')const password =useInput('')constonSubmit=(e)=>{//阻止默认事件发生
    e.preventDefault()// 获取表单值
    console.log(username.value, password.value);}return(<form onSubmit={onSubmit}><input type="text"{...username}/><input type="password"{...password}/><button type="submit">提交</button></form>);}exportdefault App;

在这里插入图片描述

React Hooks中可以对性能进行优化的函数

useMemo()

具有缓存作用,有助于避免在每次渲染时都进行高开销的计算。

const memoizedValue =useMemo(()=>computeExpensiveValue(a, b),[a, b]);

把创建函数和依赖项数组作为参数传入useMemo,当某个依赖改变时才会重新执行useMemo()函数。
如果没有提供依赖项数组,useMemo()每次渲染时都会重新执行useMemo()函数。

举例:
useMemo()监听count的值,当count的值改变时,newValue会更新。

import{ useState, useMemo}from'react';exportdefault()=>{const[count, setCount]=useState(0)const[num, setNum]=useState(0)const newValue =useMemo(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)return count+num
    },[count])return(<div><h1>{count}</h1><button onClick={()=>{setCount(count+1)}}>count +1</button><hr/><h1>{num}</h1><button onClick={()=>{setNum(num+1)}}>Num +1</button><hr/><h2>{newValue}</h2></div>)}

点击5次num+1,num变为5,虽然newValue仍然为0,但是num=5已经被缓存了;点击count+1,他会计算count此时的值1与num缓存的值5的和,最终结果newValue为6。

结果如图:
在这里插入图片描述

useCallback()

useCallback

可以说是

useMemo

的语法糖,能用

useCallback

实现的,都可以使用

useMemo

, 常用于react的性能优化

与useMemo()一样,依赖数组改变时才会重新执行useCallback()函数。
如果没有依赖数组,每次渲染都会重新执行useCallback()函数。

const memoizedCallback =useCallback(()=>{doSomething(a, b)},[a, b]);

举例:

和上述useMemo()的效果一样,区别是useCallback()调用newValue时是:

newValue()
import React,{ useState, useCallback}from'react';functionApp(){const[count, setCount]=useState(0)const[num, setNum]=useState(0)const newValue =useCallback(()=>{
        console.log(`count 值为${count}`)
        console.log(`num 值为 ${num}`)return count+num;},[count])return(<div><h1>{count}</h1><button onClick={()=>{setCount(count+1)}}>count +1</button><hr/><h1>{num}</h1><button onClick={()=>{setNum(num+1)}}>Num +1</button><hr/>{/* 调用useCallback 返回的值 */}<h2>{newValue()}</h2></div>)}exportdefault App;

useMemo()和useCallback()的区别

useMemo()返回缓存的变量(memoized)
useCallback()返回缓存的函数


本文转载自: https://blog.csdn.net/weixin_52148548/article/details/125258568
版权归原作者 甜甜酷盖 所有, 如有侵权,请联系我们删除。

“React Hooks(钩子函数)”的评论:

还没有评论