[React] react-hooks如何使用

news2024/12/23 18:25:37

react-hooks思想和初衷,也是把组件,颗粒化,单元化,形成独立的渲染环境,减少渲染次数,优化性能。

文章目录

      • 1.为什么要使用hooks
      • 2.如何使用hooks
        • 2.1 useState
        • 2.2 useEffect
        • 2.3 useLayoutEffect
        • 2.4 useRef
        • 2.5 useContext
        • 2.6 useReducer
        • 2.7 useMemo
        • 2.8 useCallback
      • 3.总结

1.为什么要使用hooks

  1. react-hooks可以让我们的代码的逻辑性更强,可以抽离公共的方法,公共组件。
  2. react-hooks思想更趋近于函数式编程。用函数声明方式代替class声明方式,虽说class也是es6构造函数语法糖,但是react-hooks写起来更有函数即组件,无疑也提高代码的开发效率(无需像class声明组件那样写声明周期,写生命周期render函数等)
  3. react-hooks可能把庞大的class组件,化整为零成很多小组件,useMemo等方法让组件或者变量制定一个适合自己的独立的渲染空间,一定程度上可以提高性能,减少渲染次数。这里值得一提的是,如果把负责 请求是数据 ➡️ 视图更新的渲染组件,用react-hooks编写的话 ,配合immutable等优秀的开源库,会有更棒的效果(这里特别注意的是⚠️,如果乱用hooks,不但不会提升性能,反而会影响性能,带来各种各样的想不到的问题)。

2.如何使用hooks

  1. useCallback

  2. useContext

  3. useEffect

  4. useLayoutEffect

  5. useMemo

  6. useReducer

  7. useRef

  8. useState

2.1 useState

数据存储,派发更新。

useState出现,使得react无状态组件能够像有状态组件一样,可以拥有自己state,useState的参数可以是一个具体的值,也可以是一个函数用于判断复杂的逻辑,函数返回作为初始值,usestate 返回一个数组,数组第一项用于读取此时的state值 ,第二项为派发数据更新,组件渲染的函数,函数的参数即是需要更新的值。useState和useReduce 作为能够触发组件重新渲染的hooks,我们在使用useState的时候要特别注意的是,useState派发更新函数的执行,就会让整个function组件从头到尾执行一次,所以需要配合useMemo,usecallback等api配合使用。

const DemoState = (props) => {
   /* number为此时state读取值 ,setNumber为派发更新的函数 */
   let [number, setNumber] = useState(0) /* 0为初始值 */
   return (<div>
       <span>{ number }</span>
       <button onClick={ ()=> {
         setNumber(number+1)
         console.log(number) /* 这里的number是不能够即使改变的  */
       } } ></button>
   </div>)
}

state的值是不能即时改变的,只有当下一次上下文执行的时候,state值才随之改变。

2.2 useEffect

useEffect 组件更新副作用钩子。

在function组件中,当组件完成挂载,dom渲染完成,做一些操纵dom,请求数据的时候, useEffect 可以优化性能。

如果我们需要在组件初次渲染的时候请求数据,那么useEffect可以充当class组件中的 componentDidMount。

如果不给useEffect执行加入限定条件,函数组件每一次更新都会触发effect ,那么也就说明每一次state更新,或是props的更新都会触发useEffect执行,此时的effect又充当了componentDidUpdate。

所以说合理的用于useEffect就要给effect加入限定执行的条件,也就是useEffect的第二个参数,这里说是限定条件,也可以说是上一次useeffect更新收集的某些记录数据变化的记忆,在新的一轮更新,useeffect会拿出之前的记忆值和当前值做对比,如果发生了变化就执行新的一轮useEffect的副作用函数,useEffect第二个参数是一个数组,用来收集多个限制条件 。

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

function ClassCmp(props) {
    const [count, setCount] = useState(0)

    // didMount, didUpdate

    // didUpdate
    useEffect(() => {
        console.log(count)
        document.title = `${count}`
               /* 定时器 延时器等 */
       const timer = setInterval(()=>console.log(666),1000)
       /* 事件监听 */
       window.addEventListener('resize', handleResize)
       /* 此函数用于清除副作用 */
       return function(){
           clearInterval(timer) 
           window.removeEventListener('resize', handleResize)
       }
    }, [count])


    return (
        <div>
            <h3>hooks</h3>
            <h3>{count}</h3>
            <button onClick={() => setCount(count + 1)}>add</button>
            <h3>{useClock().toLocaleTimeString()}</h3>
        </div>
    );
}

export default ClassCmp;

如果我们需要在组件销毁的阶段,做一些取消dom监听,清除定时器等操作,那么我们可以在useEffect函数第一个参数,结尾返回一个函数,用于清除这些副作用。相当与componentWillUnmount。

useEffect是不能直接用 async await 语法糖的。用 async effect 可以对effect进行一层包装。

const asyncEffect = (callback, deps)=>{
   useEffect(()=>{
       callback()
   },deps)
}
2.3 useLayoutEffect

渲染更新之前的 useEffect。

useEffect 执行顺序 组件更新挂载完成 -> 浏览器dom 绘制完成 -> 执行useEffect回调 。

useLayoutEffect 执行顺序 组件更新挂载完成 -> 执行useLayoutEffect回调-> 浏览器dom 绘制完成。

useLayoutEffect 代码可能会阻塞浏览器的绘制 如果我们在useEffect 重新请求数据,渲染视图过程中,肯定会造成画面闪动的效果,而如果用useLayoutEffect ,回调函数的代码就会阻塞浏览器绘制,所以可定会引起画面卡顿等效果,那么具体要用 useLayoutEffect 还是 useEffect ,要看实际项目的情况,大部分的情况 useEffect 都可以满足的。

2.4 useRef

useRef: 获取元素 ,缓存数据。

和传统的class组件ref一样,react-hooks 也提供获取元素方法 useRef,它有一个参数可以作为缓存数据的初始值,返回值可以被dom元素ref标记,可以获取被标记的元素节点。

import {useRef} from "react";

const DemoUseRef = ()=>{
    const dom= useRef(null)
    const handerSubmit = ()=>{
        /*  <div >表单组件</div>  dom 节点 */
        console.log(dom.current)
    }
    return <div>
        {/* ref 标记当前dom节点 */}
        <div ref={dom} >表单组件</div>
        <button onClick={()=>handerSubmit()} >提交</button>
    </div>
}


export default DemoUseRef;import {useRef} from "react";

const DemoUseRef = ()=>{
    const dom= useRef(null)
    const handerSubmit = ()=>{
        /*  <div >表单组件</div>  dom 节点 */
        console.log(dom.current)
    }
    return <div>
        {/* ref 标记当前dom节点 */}
        <div ref={dom} >表单组件</div>
        <button onClick={()=>handerSubmit()} >提交</button>
    </div>
}


export default DemoUseRef;

z

useRef还有一个很重要的作用就是缓存数据。

react-redux 在react-hooks发布后,用react-hooks重新了其中的Provide,connectAdvanced)核心模块,可以见得 react-hooks在限制数据更新,高阶组件上有这一定的优势,其源码大量运用useMemo来做数据判定。

      /* 这里用到的useRef没有一个是绑定在dom元素上的,都是做数据缓存用的 */
      /* react-redux 用userRef 来缓存 merge之后的 props */
      const lastChildProps = useRef()
      //  lastWrapperProps 用 useRef 来存放组件真正的 props信息
      const lastWrapperProps = useRef(wrapperProps)
      //是否储存props是否处于正在更新状态
      const renderIsScheduled = useRef(false)

react-redux中用useRef 对数据做的缓存。

//获取包装的props 
function captureWrapperProps(
  lastWrapperProps,
  lastChildProps,
  renderIsScheduled,
  wrapperProps,
  actualChildProps,
  childPropsFromStoreUpdate,
  notifyNestedSubs
) {
   //我们要捕获包装props和子props,以便稍后进行比较
  lastWrapperProps.current = wrapperProps  //子props 
  lastChildProps.current = actualChildProps //经过  merge props 之后形成的 prop
  renderIsScheduled.current = false

}

react-redux 用重新赋值的方法,改变缓存的数据源,避免不必要的数据更新, 如果选用useState储存数据,必然促使组件重新渲染 所以采用了useRef解决了这个问题。

2.5 useContext

useContext: 自由获取context, 获取父级组件传递过来的context值, 这个当前值就是最近的父级组件 Provider 设置的value值,useContext参数一般是由 createContext 方式引入 ,也可以父级上下文context传递 ( 参数为context )。useContext 可以代替 context.Consumer 来获取Provider中保存的value值。

export const MyContext = React.createContext()

function UseContextPage(props) {
    const ctx = useContext(MyContext)
    console.log(ctx)
    return (
        <div>
            <h3>UseContextPage</h3>
            <h3>{ctx.themeColor}</h3>
        </div>
    );
}

export default UseContextPage;
export const Context = React.createContext()
const DemoContext = ()=> {
    const value = useContext(Context)
    /* my name is alien */
    return <div> my name is { value.name }</div>
}

/* 用Context.Consumer 方式 */
const DemoContext1 = ()=>{
    return <Context.Consumer>
        {/*  my name is alien  */}
        { (value)=> <div> my name is { value.name }</div> }
    </Context.Consumer>
}

export default function UseContext(){
    return <div>
        <Context.Provider value={{ name:'alien' , age:18 }} >
            <DemoContext />
            <DemoContext1 />
        </Context.Provider>
    </div>
}

在这里插入图片描述

2.6 useReducer

useReducer: 无状态组件中的redux。useReducer 是react-hooks提供的能够在无状态组件中运行的类似redux的功能api, 我们可以通过中间件的方式来增强dispatch redux-thunk redux-sage redux-action redux-promise都是比较不错的中间件。

useReducer 接受的第一个参数是一个函数,我们可以认为它就是一个reducer ,reducer的参数就是常规reducer里面的state和action,返回改变后的state, useReducer第二个参数为state的初始值 返回一个数组,数组的第一项就是更新之后state的值 ,第二个参数是派发更新的dispatch函数 。

dispatch 的触发会触发组件的更新,这里能够促使组件从新的渲染的一个是useState派发更新函数,另一个就 useReducer中的dispatch。

const DemoUseReducer = ()=>{
    /* number为更新后的state值,  dispatchNumbner 为当前的派发函数 */
   const [ number , dispatchNumbner ] = useReducer((state,action)=>{
       const { payload , name  } = action
       /* return的值为新的state */
       switch(name){
           case 'add':
               return state + 1
           case 'sub':
               return state - 1 
           case 'reset':
             return payload       
       }
       return state
   },0)
   return <div>
      当前值:{ number }
      { /* 派发更新 */ }
      <button onClick={()=>dispatchNumbner({ name:'add' })} >增加</button>
      <button onClick={()=>dispatchNumbner({ name:'sub' })} >减少</button>
      <button onClick={()=>dispatchNumbner({ name:'reset' ,payload:666 })} >赋值</button>
      { /* 把dispatch 和 state 传递给子组件  */ }
      {/*<MyChildren  dispatch={ dispatchNumbner } State={{ number }} />
   </div>
}

在这里插入图片描述

2.7 useMemo

useMemo: 能形成独立的渲染空间,能够使组件,变量按照约定好规则更新。memo的作用结合了pureComponent纯组件和 componentShouldUpdate功能。会对传进来的props进行一次对比,然后根据第二个函数返回值来进一步判断哪些props需要更新。

function UseMemoPage(props) {
    const [count, setCount] = useState(0)
    const [name, setName] = useState("")

    // 依赖于count改变, 才触发此函数
    const total = useMemo(() => {
        console.log("@")

        let res = 0;
        for (let i = 0; i < count; i++){
            res += i
        }

        return res
    }, [count])

    return (
        <div>
            <h3>UseMemoPage</h3>
            <h3>{total} - {count}- {name}</h3>
            <button onClick={() => {setCount(count + 1)}}>add</button>
            <input value={name} onChange={event => {setName(event.target.value)}}/>
        </div>
    );
}

export default UseMemoPage;
  1. useMemo可以减少不必要的循环,减少不必要的渲染。
  2. useMemo可以减少子组件的渲染次数。
  3. useMemo让函数在某个依赖项改变的时候才运行, 可以避免不必要的开销。

如果我们应用useMemo根据依赖项合理的颗粒化我们的组件,能起到很棒的优化组件的作用。

2.8 useCallback

useMemo和useCallback接收的参数都是一样,都是在其依赖项发生变化后才执行,都是返回缓存的值,区别在于useMemo返回的是函数运行的结果,useCallback返回的是函数。

这个回调函数是经过处理后的也就是说父组件传递一个函数给子组件的时候,由于是无状态组件每一次都会重新生成新的props函数,这样就使得每一次传递给子组件的函数都发生了变化,这时候就会触发子组件的更新,这些更新是没有必要的,此时我们就可以通过usecallback来处理此函数,然后作为props传递给子组件。

/* 用react.memo */
const DemoChildren = React.memo((props)=>{
   /* 只有初始化的时候打印了 子组件更新 */
    console.log('子组件更新')
   useEffect(()=>{
       props.getInfo('子组件')
   },[])
   return <div>子组件</div>
})

const DemoUseCallback=({ id })=>{
    const [number, setNumber] = useState(1)
    /* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
     经过处理赋值给 getInfo */
    const getInfo  = useCallback((sonName)=>{
          console.log(sonName)
    },[id])
    return <div>
        {/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
        <button onClick={ ()=>setNumber(number+1) } >增加</button>
        <DemoChildren getInfo={getInfo} />
    </div>
}

useCallback ,必须配合 react.memo pureComponent ,否则不但不会提升性能,还有可能降低性能。

3.总结

react-hooks的诞生,也不是说它能够完全代替class声明的组件,对于业务比较复杂的组件,class组件还是首选,只不过我们可以把class组件内部拆解成funciton组件,根据业务需求,哪些负责逻辑交互,哪些需要动态渲染,然后配合usememo等api,让性能提升起来。react-hooks使用也有一些限制条件,比如说不能放在流程控制语句中,执行上下文也有一定的要求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1038898.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【前段基础入门之】=>HTML5 的新增特性!

这里写目录标题 HTML5简介HTML5 优势新增语义化标签新增布局标签新增状态标签meter 标签progress 标签 新增列表标签新增文本标签文本注音文本标记 新增表单功能表单控件新增属性input 新增属性值form 标签新增属性 新增多媒体标签视频播放标签音乐播放标签 新增全局属性&#…

ICCV 2023 | 噪声关联鲁棒的图匹配方法

©PaperWeekly 原创 作者 | 林义杰 单位 | 四川大学 研究方向 | 多模态、多视角学习 论文标题&#xff1a; Graph Matching with Bi-level Noisy Correspondence 论文地址&#xff1a; https://arxiv.org/pdf/2212.04085.pdf 开源代码&#xff1a; https://github.com/XLe…

word文档莫名其妙的丢失了怎么办?7个方案恢复

不知道你是否曾经遇到过相似的情况&#xff1a;花费了数小时甚至数天编辑的Word文档&#xff0c;却莫名其妙的丢失了。这时的心情可能非常复杂。如果你不知道该怎么办&#xff0c;以下是几种恢复方案&#xff0c;希望能对你有所帮助&#xff01; 关于Word文档 Word文档通常是由…

Vue路由与nodejs下载安装及环境变量的配置

目录 前言 一、Vue路由 1.路由简介 是什么 作用 应用场景 2.SPA简介 SPA是什么 SPA的优点 注意事项 3.路由实现思路 1.引入路由的js依赖 2.定义组件 3.定义组件与路径的对应关系 4.通过路由关系获取路由对象router 5.将路由对象挂载到实例中 6.触发路由事…

学生选课系统基础版

目录 一.Java 中的集合框架&#xff08;上&#xff09; 1.Java中的集合框架概述 2.Collection接口&接口简介 3.学生选课——创建学生类和课程类 4.学生选课——添加课程Ⅰ 5.学生选课——添加课程Ⅱ 6.学生选课——课程查询 7.学生选课——课程修改 8.学生选课——课程删…

Java日志源码详解,SpringBoot日志 slf4j、logback、log4j

日志视频讲解—上日志视频讲解—下学习文档集合 一、前提 在Java中说起日志&#xff0c;定听过这样几个名词&#xff1a;slf4j、logback、log4j&#xff0c;在正式开始之前&#xff0c;先了解几个简单的概念 slf4j、logback、log4j 的作者都是一个人slf4j 的全名是 Simple Log…

ByteTrack 论文学习

1. 解决了什么问题&#xff1f; 多目标跟踪是在给定的视频片段中&#xff0c;预测出目标的边框和 ID 信息。现有方法需要在 true positives 和 false positives 之间做取舍&#xff0c;将高于一定阈值的检测框关联起来&#xff0c;获取其 ID。而那些低得分的目标&#xff08;如…

全新的Windows12上线抢先体验

AIGC专栏/AI绘画教程/java面试题领取 win12太离谱了&#xff0c;win11还没用几天&#xff0c;win12就已经出来了&#xff0c;如此流畅的页面&#xff0c;很具有和苹果一拼的效果&#xff0c;流畅度也是一流。文末有领取方式。 WIN12系统在色差表现方面也超越了苹果。它采用了前…

华为数通方向HCIP-DataCom H12-831题库(单选题:141-160)

第141题 R3与R1的IS-IS邻居没有建立,根据本图的信息,可能的原因是? A、R3与R1的IS-Level不匹配 B、R3与R1的互连接口circuit-type不匹配 C、R3与R1的IIH认证失 D、R3与R1的System ID重复 答案: B 解析: 从上图的Bad Circuit Type:16 可知道R3与R1的互连接口circuit-type…

【EI会议征稿】第三届计算机图形学、人工智能与数据处理国际学术会议 (ICCAID 2023)

第三届计算机图形学、人工智能与数据处理国际学术会议 2023 3rd International Conference on Computer Graphics, Artificial Intelligence and Data Processing (ICCAID 2023) 第三届计算机图形学、人工智能与数据处理国际学术会议&#xff08;ICCAID 2023&#xff09;将于…

基于数据驱动的成本洞察,趣丸科技的FinOps进阶之路~

今年以来&#xff0c;我们注意到越来越多的单位开始积极实践FinOps&#xff0c;而随着FinOps的发展&#xff0c;大家对于其落地过程的关注也更加具体和深入&#xff0c;涉及了账单波动、FinOps的边际效应、成本模型、依赖工具等多个关键问题。 本月「UGeek大咖说」线上直播活动…

机器学习之泛化与过拟合的概念

文章目录 泛化&#xff08;Generalization&#xff09;&#xff1a;过拟合&#xff08;Overfitting&#xff09;&#xff1a;例子 泛化&#xff08;Generalization&#xff09;&#xff1a; 泛化是指机器学习模型在未见过的新数据上表现良好的能力。换句话说&#xff0c;一个好…

【软件测试】Junit5

Selenium自动化测试框架Junit单元测试框架拿着一个技术写自动化测试用例 (Selenium3)拿着一个技术管理已经编写好的测试用例 (Junit5) Junit相关技术 Junit是针对java的一个单元测试框架。 注解 Test 表示当前的这个方法是一个测试用例 添加依赖&#xff1a; 不需要main方…

解决react使用redux toolkits时出现的数组对象长度始终为0的怪异问题

有个react项目在添加购物车后&#xff0c;立马白屏&#xff0c;看一下console报错properties of undefined(reading length) 那意思是说数组没有长度&#xff0c;然后定位Header.tsx的182行&#xff0c;果然是数组长度报错 回到具体代码中&#xff1a;发现shoppingCartItems实…

大模型存在“反转诅咒”现象,无法处理反向问题;Langchain课程资源

&#x1f989; AI新闻 &#x1f680; 大模型存在“反转诅咒”现象&#xff0c;无法处理反向问题 摘要&#xff1a;最新研究发现&#xff0c;大语言模型存在“反转诅咒”现象&#xff0c;即明知道“A 是 B”&#xff0c;却答不出“B 是 A”。研究人员进行了两项实验&#xff0…

【RocketMQ专题】快速实战及集群架构原理详解

目录 课程内容一、MQ简介基本介绍*作用&#xff08;解决什么问题&#xff09; 二、RocketMQ产品特点2.1 RocketMQ介绍2.2 RocketMQ特点2.3 RocketMQ的运行架构2.4 消息模型 三、RocketMQ快速实战3.1 快速搭建RocketMQ服务3.2 快速实现消息收发3.3 搭建Maven客户端项目3.4 搭建R…

Linear Feedback Shift Register

线性反馈移位寄存器&#xff08;Linear Feedback Shift Register&#xff0c;简称LFSR&#xff09;是一种数字电路设计和密码学中常用的寄存器类型。它是一种简单而高效的方式&#xff0c;用于生成伪随机的二进制序列&#xff0c;并在数据混淆、错误检测和加密等领域中有应用。…

怎么用蜂邮EDM和Outlook批量发送邮件带附件

蜂邮EDM和Outlook批量发送邮件带附件的流程&#xff1f;有哪些邮件批量发送邮件附件的方法&#xff1f; 在现代社会中&#xff0c;电子邮件是一种广泛应用的沟通工具&#xff0c;而批量发送邮件带附件则是许多商业和个人用户的常见需求。本文将介绍如何使用蜂邮EDM和Outlook这…

时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-BiLSTM鹈鹕算法优化卷积双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-BiLSTM鹈鹕算…

吉力宝:智能科技鞋品牌步力宝引领传统产业创新思维

在现代经济环境下&#xff0c;市场经济下产品的竞争非常的激烈&#xff0c;如果没有营销&#xff0c;产品很可能不被大众认可&#xff0c;酒香也怕巷子深&#xff0c;许多传统产业不得不面临前所未有的挑战。而为了冲出这个“巷子”&#xff0c;许多企业需要采用创新思维&#…