[React] react-hooks如何使用

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// didUpdateuseEffect(() => {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 之后形成的 proprenderIsScheduled.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 + 1case '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.xdnf.cn/news/140764.html

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

相关文章

【网络编程】TCP Socket编程

TCP Socket编程 1. ServerSocket2. Socket3. TCP的长短连接4. Socket 通信模型5. 代码示例&#xff1a;TCP 回显服务器 流套接字&#xff1a; 使用传输层TCP协议 TCP: 即Transmission Control Protocol&#xff08;传输控制协议&#xff09;&#xff0c;传输层协议。 TCP的特点…

【计算机网络】IP协议(下)

文章目录 1. 特殊的IP地址2. IP地址的数量限制3. 私有IP地址和公网IP地址私有IP为什么不能出现在公网上&#xff1f;解决方案——NAT技术的使用 4. 路由5. IP分片问题为什么要进行切片&#xff1f;如何做的分片和组装&#xff1f;16位标识3位标志13位片偏移例子 细节问题如何区…

基于springboot地方废物回收机构管理系统springboot11

大家好✌&#xff01;我是CZ淡陌。一名专注以理论为基础实战为主的技术博主&#xff0c;将再这里为大家分享优质的实战项目&#xff0c;本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#xff0c;希望你能有所收获&#xff0c;少走一些弯路…

智慧农业农场小程序源码 智慧农场系统源码

智慧农业农场小程序源码 智慧农场系统源码 一、 智慧农场系统的组成 智慧农场系统一般包括传感器、控制器、数据采集与处理平台、应用软件等组成部分。其中, 传感器主要用于采集土壤温度、湿度、光照强度等环境参数,以及作物生长状态、水肥情况等生产信息。控制器则根据传感器…

GLTF编辑器的另一个作用

1、GLB模型介绍 GLB&#xff08;GLTF Binary&#xff09;是一种用于表示三维模型和场景的文件格式。GLTF是"GL Transmission Format"的缩写&#xff0c;是一种开放的、跨平台的标准&#xff0c;旨在在各种3D图形应用程序和引擎之间进行交换和共享。 GLB文件是GLTF文件…

PyCharm 手动下载插件

插件模块一直加载失败&#xff0c;报错信息&#xff1a; Marketplace plugins are not loaded. Check the internet connection and refresh. 尝试了以下方法&#xff0c;均告失败&#xff1a; pip 换源Manage Plugin Repositories...HTTP 代理设置...关闭三个防火墙 最后选…

RK3568平台开发系列讲解(工具命令篇)ADB的安装

🚀返回专栏总目录 文章目录 一、ADB介绍二、Windows 下安装 adb 工具沉淀、分享、成长,让自己和他人都能有所收获!😄 一、ADB介绍 adb 全称 Android Debug Bridge,直译过来就是 Android 调试桥,它是一个通用的命令行工具。adb 做为 Android 设备与 PC 端连接的一个桥梁…

MissionPlanner编译过程

环境 windows 10 mission planner 1.3.80 visual studio 2022 git 2.22.0 下载源码 (已配置git和ssh) 从github上克隆源码 git clone gitgithub.com:ArduPilot/MissionPlanner.git进入根目录 cd MissionPlanner在根目录下的ExtLibs文件下是链接的其它github源码&#xff0…

MySQL 高级(进阶) SQL 语句(二) -----存储过程

目录 1 存储过程 1.1 创建存储过程​ 1.2 调用存储过程 1.3 查看存储过程 1.4 存储过程的参数 1.5 修改存储过程 1.6 删除存储过程 2 条件语句 3 循环语句 1 存储过程 存储过程是一组为了完成特定功能的SQL语句集合。 存储过程在使用过程中是将常用或者复杂的工作预…

ClickHouse分布式集群部署

目录 ​编辑 一、环境说明 二、安装部署 2.1 RPM方式安装 2.1.1 安装yum-utils 2.1.2 配置yum repo源 2.1.3 yum install 下载安装clickhouse 2.2 信息配置 2.2.1 配置外网可访问地址 2.2.2 修改存储路径 2.2.2.1 新建存储目录 2.2.2.2 授权 2.2.2.3 修改配置 2.…

单片机第三季-第三课:STM32开发板原理图、配置、浮点运算单元

目录 1&#xff0c;开发板原理图 2&#xff0c;浮点运算单元&#xff08;FPU&#xff09; 1&#xff0c;开发板原理图 课程视频比较早&#xff0c;介绍了三款开发板。观看视频时用的开发板说和51单片机共板的STM32核心板&#xff0c;将51单片机从底座拆下来后&#xff0c;安…

【从0学习Solidity】35. 荷兰拍卖

【从0学习Solidity】35. 荷兰拍卖 博主简介&#xff1a;不写代码没饭吃&#xff0c;一名全栈领域的创作者&#xff0c;专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构&#xff0c;分享一些项目实战经验以及前沿技术的见解。关注我们的主页&#xff0c;探索全栈…

黑马JVM总结(十四)

&#xff08;1&#xff09;分代回收_1 Java虚拟机都是结合前面几种算法&#xff0c;让他们协同工作&#xff0c;具体实现是虚拟机里面一个叫做分代的垃圾回收机制&#xff0c;把我们堆内存大的区域划分为两块新生代、老年代 新生代有划分为伊甸园、幸存区Form、幸存区To 为什…

ARMv8 cache的包含策略inclusive 和 exclusive之间的区别以及Cortex-A55示例详解

Inclusive 和 Exclusive 一&#xff0c; 什么是cache的inclusive 和 exclusive二&#xff0c;Inclusive 和 Exclusive cache示例2.1 Inclusive cache2.2 Exclusive cache 三&#xff0c; inclusive cache和 exclusive cache的比较3.1 cache coherency3.2 miss rate3.3 cache ca…

使用 Docker 安装 Elasticsearch (本地环境 M1 Mac)

Elasticsearchkibana下载安装 docker pull elasticsearch:7.16.2docker run --name es -d -e ES_JAVA_OPTS“-Xms512m -Xmx512m” -e “discovery.typesingle-node” -p 9200:9200 -p 9300:9300 elasticsearch:7.16.2docker pull kibana:7.16.2docker run --name kibana -e EL…

最频繁被问到的SQL面试题

面试感叹失败的原因可能有很多&#xff0c;而做成的道路只有⼀条&#xff0c;那就是不断积累。纯手工的8291字的SQL面试题总结分享给初学者&#xff0c;俗称八股文&#xff0c;期待对新手有所帮助。 窗口函数题 窗口函数其实就是根据当前数据, 计算其在所在的组中的统计数据。…

网工基础知识——以太网

1972年Bob Metcalfe“以太网之父”被Xerox雇佣为网络专家&#xff0c;Bob Metcalfe 来到Xerox公司的Palo Alto研究中心&#xff08;PARC&#xff09;的第一个任务是把Palo Alto的计算机连接到ARPANET&#xff08;Internet的前身&#xff09;上。1972年底Bob Metcalfe以ALOHA系统…

嵌入式开发笔记:STM32的外设GPIO知识学习

GPIO简介&#xff1a; • GPIO &#xff08; General Purpose Input Output &#xff09;通用输入输出口 • 可配置为 8 种输入输出模式 • 引脚电平&#xff1a; 0V~3.3V &#xff0c;部分引脚可容忍 5V &#xff08;如舵机和驱动直流电机&#xff09; • 输出模式下可控制端口…

机器学习 day35(决策树)

决策树 上图的数据集是一个特征值X采用分类值&#xff0c;即只取几个离散值&#xff0c;同时也是一个二元分类任务&#xff0c;即标签Y只有两个值 上图为之前数据集对应的决策树&#xff0c;最顶层的节点称为根节点&#xff0c;椭圆形节点称为决策节点&#xff0c;矩形节点称…

springboot整合返回数据统一封装

1、MagCode&#xff0c;错误码枚举类 package com.mgx.common.enums;import lombok.*; import lombok.extern.slf4j.Slf4j;/*** 错误码* author mgx*/ Slf4j NoArgsConstructor AllArgsConstructor public enum MsgCode {/*** 枚举标识&#xff0c;根据业务类型进行添加*/Code…