当前位置: 首页 > news >正文

React 18中hook函数详解之useState和useEffect

前言

React创建组件的方式有三种,分别是函数式组件、类组件,还有createElement组件。react v16.8版本之前函数式组件是没有状态的。但是,自16.8以后得版本有个hook函数,函数式组件也有了状态,反而类组件没有多少人写了,原因在于生命周期很麻烦,也难记。笔者近几年写React项目已经很少使用类组件了。接下来,详细探讨下hook函数,为什么会有那么神奇的效果?

一 、常用的Hook有哪些?

React Hooks 是 React v16.8 之后推出的函数式组件状态管理方案。它是为了解决状态复用、类组件写法麻烦等原因而提出的,Hook函数本质是闭包。Hooks 主要是利用闭包来保存状态,使用链表保存一系列 Hooks,将链表中的第一个 Hook 与 Fiber 关联。在 Fiber 树更新时,就能从 Hooks 中计算出最终输出的状态和执行相关的副作用。Hook函数带来便利有逻辑复用、业务代码更聚合、写法更简洁。

常用的Hook函数useState、useEffect、useRef、useCallback、useMemo、useReducer、useLayoutEffect

二、useState详解

useState是React自带的一个Hook函数,使用useState可声明内部状态变量。useState接收的参数为状态初始值或状态初始化方法,它返回一个数组。数组的第一项是当前状态值,每次渲染其状态值可能都会不同;第二项是可改变对应状态值的set函数,在useState初始化后该函数不会变化。

useState的类型为:

function useState<S>(initialState:S|(() => S )): [S,Dispatch <SetStateAction <S>>];

initialState仅在组件初始化时生效,后续的渲染将忽略initialState:

const [value, setValue] = useState("");
const [count, setCount] = useState(value);

如上例中的value,当初始值传入另一个状态并初始化后,另一个状态函数将不再依赖value的值。

在 class 中,我们需要调用 this.setState() 来更新 count 值,在函数式组件中,我们已经有了 setCount 和 count 变量,所以我们不需要 this:

import {useState} from "react";
const Example = () => {const [count, setCount] = useState(0);return (<div><button onClick={() => {setCount(count+1)}}>点击更新count:{count}     </button></div>)
}

类似于setState,单击按钮时调用setCount更新了状态值count。当调用setCount后,组件会重新渲染,count的值会得到更新。

当传入初始状态为函数时,其仅执行一次,类似于类组件中的构造函数:

useState返回的更新函数也可使用函数式更新:

setCount(preCount => preCount + 1)

如果新的state需要依赖先前的 state 计算得出,那么可以将回调函数当作参数传递给setState。该回调函数将接收先前的state,并将返回的值作为新的state进行更新。

在组件生命周期或React合成事件中,setState是异步;在setTimeout或者原生dom事件中,setState是同步。

为什么react大部分情况setState是异步的呢?假如所有setState是同步的,意味着每执行一次setState时(有可能一个同步代码中,多次setState),都重新vnode diff + dom修改,这对性能来说是极为不好的。如果是异步,则可以把一个同步代码中的多个setState合并成一次组件更新。

三、useEffect详解

useEffect函数会在组件渲染完毕后,执行和渲染无关的副作用,如数据获取,设置订阅以及手动更改 React 组件中的 DOM 等。

有了useEffect,我们可以在函数组件中实现 像类组件中的生命周期那样某个阶段做某件事情,具有:

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

基本用法:

useEffect(() => {console.log('这是一个不含依赖数组的useEffect,每次render都会执行!')
})

useEffect接受一个回调函数和一个可选的依赖项数组。回调函数会在组件挂载、更新和卸载时执行,根据依赖项的不同情况选择执行。如果依赖项为空数组,那么回调函数只会在组件挂载和卸载时执行一次。如果依赖项中包含某个状态或属性,那么只有在这个状态或属性发生变化时才会执行回调函数。 

useEffect的规则

  • 没有传第二个参数时,在每次 render 之后都会执行 useEffect中的内容
  • useEffect接受第二个参数来控制跳过执行,下次 render 后如果指定的值没有变化就不会执行
  • useEffect 是在 render 之后浏览器已经渲染结束才执行
  • useEffect 的第二个参数是可选的,类型是一个数组
  • 根据第二个参数的不同情况,useEffect具有不同作用

下面是useEffect的一些常见用法:

1、获取数据和执行网络请求:

import React, { useState, useEffect } from 'react';
import axios from 'axios';function MyComponent() {const [data, setData] = useState([]);useEffect(() => {const fetchData = async () => {const result = await axios.get('/api/data');setData(result.data);}fetchData();}, []);return (<div>{data.map((item) => <div key={item.id}>{item.name}</div>)}</div>);
}export default MyComponent;

2、订阅事件:

import { useEffect } from 'react';function MyComponent() {useEffect(() => {const subscription = myEventEmitter.subscribe('event', () => {// 处理事件逻辑});return () => {subscription.unsubscribe();};}, []);return <div>My Component</div>;
}

3、执行清理操作:

import { useEffect, useState } from 'react';function MyComponent() {const [timer, setTimer] = useState(null);useEffect(() => {const id = setInterval(() => {// 处理定时器逻辑}, 1000);setTimer(id);return () => {clearInterval(timer);};}, []);return <div>My Component</div>;
}

4、使用第三方库:

import { useEffect } from 'react';
import moment from 'moment';function MyComponent() {useEffect(() => {moment.locale('zh-cn');}, []);return <div>{moment().format('LLLL')}</div>;
}

需要注意的是,useEffect回调函数中的操作可能会对应用程序的性能产生影响。如果回调函数执行的操作很耗费资源,那么可能会导致应用程序变慢。因此,需要谨慎使用useEffect,并在需要时进行性能优化。

在使用React Hooks时,需要遵守以下准则及特性要求。

  • 只在顶层使用Hooks。不要在循环、条件或嵌套函数中调用Hooks,确保总是在React函数组件的顶层调用它们。

  • 不要在普通的JavaScript函数中调用Hooks。仅在React的函数组件中调用Hooks,以及在自定义Hook中调用其他Hooks。

相关文章:

  • Leetcode第35题:搜索插入位置
  • 【漏洞复现】WordPress Plugin NotificationX 存在sql注入CVE-2024-1698
  • Vue2(十一):全局事件总线、消息订阅与发布pubsub、TodoList的编辑功能、$nextTick、过渡与动画
  • 3-26 备赛
  • Java内存模型简述
  • 前段项目结构
  • 7-24 约分最简分式(PTA)
  • ES聚合查询
  • Vue3更新Package.json版本号
  • 海外云手机如何帮助亚马逊引流?
  • 自定义类型(2)
  • 各城市宗族文化姓氏占比数据
  • 微服务篇:设计一个注册中心和配置中心需要从哪些方面入手
  • 工具 - DBeaver 的简单使用
  • 代码随想录算法训练营第三十五天 | LeetCode860.柠檬水找零、406.根据身高重建队列 、 452. 用最少数量的箭引爆气球
  • 【刷算法】求1+2+3+...+n
  • ES6之路之模块详解
  • JAVA 学习IO流
  • Laravel Telescope:优雅的应用调试工具
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • Lucene解析 - 基本概念
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • SegmentFault 2015 Top Rank
  • SpringBoot几种定时任务的实现方式
  • Traffic-Sign Detection and Classification in the Wild 论文笔记
  • vue--为什么data属性必须是一个函数
  • 笨办法学C 练习34:动态数组
  • 工作中总结前端开发流程--vue项目
  • 基于HAProxy的高性能缓存服务器nuster
  • 简单实现一个textarea自适应高度
  • 详解NodeJs流之一
  • C# - 为值类型重定义相等性
  • 如何用纯 CSS 创作一个菱形 loader 动画
  • 说说我为什么看好Spring Cloud Alibaba
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • (1)(1.13) SiK无线电高级配置(五)
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (C语言)fread与fwrite详解
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (LeetCode) T14. Longest Common Prefix
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)计算机毕业设计ssm高校《大学语文》课程作业在线管理系统
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (一) storm的集群安装与配置
  • (转)C#调用WebService 基础
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • ***php进行支付宝开发中return_url和notify_url的区别分析
  • .NET 8.0 中有哪些新的变化?
  • .net操作Excel出错解决
  • .Net的C#语言取月份数值对应的MonthName值
  • .NET构架之我见
  • .NET框架设计—常被忽视的C#设计技巧