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

React基础知识四 Hooks

什么是hooks? (coderwhy)

hooks是react 16.8(2019年)出的新特性。
react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候,函数式组件是非常鸡肋的,几乎没什么用。因为函数式组件不能保存数据状态,所以只能用于一些简单的展示的场景,传什么数据就展示什么数据(因为只有props是可以用的)。并且函数组件是没有生命周期的。

但因为函数式编程的思想在前端是普遍流行和推崇的。我猜想react团队在设计函数式组件的时候肯定已经想到了这个问题。但可能当时没有想到合适方式实现函数式组件的完整功能。

对于开发者来说,使用类式组件或者函数式组件来开发功能实际上都是无所谓,谁好用就用谁。但设计者为了实现函数式组件可以说是绞尽脑汁。至于设计出来的东西好不好用另说。但函数式组件的这条路是一定要走下去的。

还有一个促使hooks诞生的原因是类式组件存在一些缺点。例如类式不好对功能进行拆分。当然hooks本身是否存在别的缺点我们另说。class概念难以理解以及this的指向问题处理对于初学者来说都是比较麻烦的。

1.hooks是完全可选的,你不用hooks,用类式组件也是完全没有问题的。
2.hooks是100%向后兼容的。hooks不包含任何破坏性改动。

3.hooks的代码比类组件相对少一些。

useState和useEffect的说明

你只要把这两个hooks学了,已经可以应对90%的情况了,因为这两个hooks可以处理state和生命周期问题。

useState的使用和使用规则

useState是一个hook,但他和react的state本质是没什么关系的,只是功能上是一样的——有一个地方能存储组件的数据。

基本使用

我们定义一个useState。数组的第一个参数是变量的名字,第二个名字是用于修改变量的函数,可以传一个初始值。

这是hooks的基本使用规则,都是这样使用的。

要修改值,我们直接调用自己定义的setCount,把原来的count和num相加,就实现了计算器的效果。

import { useState } from "react";function App() {const [count, setCount] = useState(0);function handleAdd(num) {setCount(count + num);}return (<div className="App"><div>{count} <button onClick={(e) => handleAdd(1)}>+1</button></div></div>);
}export default App;

我们可以只写一个变量名,不写set方法,其实数组的两个变量,就是变量的get和set方法。

import { useState } from "react";
function App() {const [user] = useState({ name: "Tom", age: 18 });//const [user,setName] = useState({ name: "Tom", age: 18 });return (<div className="App"><div>{user.name + " " + user.age}</div></div>);
}export default App;

使用规则

不是所有的地方都可以使用hooks,hooks必须定义在函数顶层,不能定义在if,for里面。官方就是这样规定的。

hooks简单原理

在函数组件执行完成后,count变量已经被销毁了,但是count的值被react保留了起来,当页面再次渲染的时候,会重新创建一个count变量,并把原来的值赋值给新创建的这个count变量。

const [count, setCount] = useState(0);

useEffect的使用

什么是useEffect

effect的意思是影响,效果,效应。其实说简单点,这个hooks就是用来监听useState的数据变化的。同时可以实现类似生命周期的效果,但实际上,生命周期效果只是useEffect的一个特性,最直观的作用还是监听数据的变化。

useEffect基本使用

下面的代码实现一个计算器。

import { useEffect, useState } from "react";function App(props) {const [count, setCount] = useState(100);useEffect(() => {console.log(count);});function handleAdd(num) {setCount(count + num);}return (<div>App:{count}<button onClick={(e) => handleAdd(1)}>+1</button></div>);
}export default App;

第一次页面渲染的时候,useEffect里面的log输出了。
在这里插入图片描述
我们点击按钮做累加。useEffect回调函数被多次执行了。但这和count这个setState是没有关系的。也就是说,只要页面重新渲染了,useEffect就会重新执行。

啥?那useEffect有什么用?别着急,下一小节你就明白了。
在这里插入图片描述

多个useEffect的使用和绑定setState变量

上面的例子可能挺让人困惑的,到底是要干什么?下面就来好好说说useEffect的本质是什么。看完这个小节,就能很好理解useEffect是怎么用的了。

我们定义两个useState变量,count和user。并且定义两个useEffect方便绑定这两个useState。
useEffect接收两个参数,第一个参数就是我们前面用的回调函数,第二个参数是一个数组,里面放useState产生的变量,放什么变量,回调函数就只会在放的变量发生改变的时候回调。

import { useEffect, useState } from "react";function App(props) {const [count, setCount] = useState(100);const [user, setUser] = useState({ name: "Tom", age: 18 });useEffect(() => {console.log("监听count:" + count);}, [count]);useEffect(() => {console.log("监听user:", user);}, [user]);function handleAdd(num) {setCount(count + num);}return (<div>App:{count}<button onClick={(e) => handleAdd(1)}>+1</button></div>);
}export default App;

页面加载的时候,我们可以看到两个useEffect输出下面的内容。
在这里插入图片描述
当点击+1的时候,count的useEffect回调函数执行了,而user的useEffect回调函数没有执行。这说明,在指定了第二个参数后,useEffect的回调函数只会在第二个参数的值发生变化的时候才会回调。
在这里插入图片描述
假设写成下面这个样子,那么在user或者count发生改变的时候,回调函数会被执行。

  useEffect(() => {}, [user,count]);

假如写成下面这个样子,那么user,count发生变化,这个函数都不会执行。也就是任何useState发生变化,这个函数都不会回调。但是在第一次渲染的时候函数还是会回调的。

  useEffect(() => {}, []);

假如写成下面这个样子,也就是最开始的代码,又是怎么回事呢?
写成这样表示,任何useState发生变化,这个函数都会回调。

  useEffect(() => {});

有什么不明白的请参考小结。

useEffect的返回值

返回值是一个回调函数,这个函数在组件被销毁或者监听的数据更新的时候回调。

  useEffect(() => {return ()=>{}});

实用技巧一 充当componentDidMount和componentWillUnmount

首先,我们第二个参数写空数组,这样任何useState的更新都不会触发这个useEffect。但是在组件第一次渲染和销毁的时候,会分别执行下面两个位置的代码,也就正好和componentDidMount和componentWillUnmount这两个生命周期的执行时机是一样的。

这是useEffect的使用技巧,react作者是否有意这样设计我也不是很清楚。

  useEffect(() => {//这里相当于componentDidMountreturn ()=>{//这里相当于componentWillUnmount}}, []);

实用技巧二 充当componentDidUpdate

因为没有填第二个参数,所以在有任何数据更新的时候都会触发这个方法,相当于是componentDidUpdate生命周期

  useEffect(() => {//有数据更新的时候触发});

小结

1.useEffect主要目的是监听useState数据的变化。
2.useEffect充当生命周期只是他的附加功能。

useContext的使用(掌握)

定义两个Context。

import { createContext } from "react";const ThemeContext = createContext();
const UserContext = createContext();export { ThemeContext, UserContext };

使用context并给初始值。

root.render(// <React.StrictMode><UserContext.Provider value={{ name: "Tom", age: 18 }}><ThemeContext.Provider value={{ color: "red", fontSize: 18 }}><App /></ThemeContext.Provider></UserContext.Provider>// </React.StrictMode>
);

通过useContext直接获取到context的值就可以直接使用了。
如果不用useContext,你就要使用xxxContext.Consumer来使用,多个context的时候还要嵌套。这里就体现了hooks的一个优点。

import { useContext } from "react";
import { ThemeContext, UserContext } from "./context";function App(props) {const user = useContext(UserContext);const theme = useContext(ThemeContext);return (<div>App:<div>user:{user.name + " " + user.age}</div><div style={{ color: theme.color, fontSize: theme.fontSize }}>theme</div></div>);
}
export default App;

useReducer的使用(了解)

这个东西在数据比较复杂的时候,有点用处,但本身用法也复杂。不推荐使用。有用到再说。

useCallback和useMemo的使用

这两个hooks是用来做性能优化的。

useCallback的使用

在将一个函数传递给子组件的时候,用useCallback做性能优化。

useRef的使用

和类组件的createRef是非常类似的,只是我们用useRef。

在函数组件里面,ref返回的值始终是同一个对象。

import { memo, useRef } from "react";const App = memo((props) => {const refBtn = useRef();const refInput = useRef();function handleClick() {//获取到组件自身console.log(refBtn.current);//获取input焦点refInput.current.focus();}return (<div>App<button ref={refBtn} onClick={handleClick}>点击</button><input ref={refInput}></input></div>);
});
export default App;

自定义hooks

自定义hook监听生命周期

其实就是把useEffect封装了一下,这样就可以监听组件的创建和销毁了。

function useLogLife(name = "") {useEffect(() => {console.log(name, "组件被创建了");return () => {console.log(name, "组件被销毁了");};}, []);
}

这个例子可以在show变化的时候创建和销毁About和Home组件。

import { memo, useEffect, useState } from "react";function useLogLife(name = "") {useEffect(() => {console.log(name, "组件被创建了");return () => {console.log(name, "组件被销毁了");};}, []);
}const About = memo((props) => {useLogLife("about");return <div>About</div>;
});const Home = memo((props) => {useLogLife("home");return <div>Home</div>;
});const App = memo((props) => {useLogLife("app");const [show, setShow] = useState(false);return (<div>App Component{show ? <Home /> : ""}{!show ? <About /> : ""}<button onClick={(e) => setShow(!show)}>点击</button></div>);
});
export default App;

获取context

首先我们定义context,和原来没什么区别。

import { createContext } from "react";const UserContext = createContext();
const TokenContext = createContext();export { UserContext, TokenContext };

提供初始数据

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./自定义hook获取context/App";
import { TokenContext, UserContext } from "./自定义hook获取context/context";const root = ReactDOM.createRoot(document.getElementById("root"));root.render(// <React.StrictMode><UserContext.Provider value={{ name: "Tom", age: 18 }}><TokenContext.Provider value={"this_is_token"}><App /></TokenContext.Provider></UserContext.Provider>// </React.StrictMode>
);

我们自定义一个useUserToken,把UserContext和TokenContext通过useContext套一层,这样我们就不需要使用consumer了。

import { useContext } from "react";
import { TokenContext, UserContext } from ".";
function useUserToken() {const user = useContext(UserContext);const token = useContext(TokenContext);return [user, token];
}
export default useUserToken;

最后,我们只需要使用我们自定义的useUserToken就可以同时获取到user和token的数据了。

import { memo } from "react";
import useUserToken from "./context/useUserToken";const Home = memo((props) => {const [user, token] = useUserToken();return <div>Home: {user.name + " " + token}</div>;
});const About = memo((props) => {const [user, token] = useUserToken();return <div>About: {user.name + " " + token}</div>;
});const App = memo((props) => {const [user, token] = useUserToken();return (<div>App: {user.name + " " + token}<Home /><About /></div>);
});
export default App;

监听窗口滚动位置

定义一个下面这样的useEffect,在组件里面直接调用window.scrollX, window.screenY就可以直接用了。

  useEffect(() => {function handleScroll(event) {console.log(window.screenX, window.screenY);}window.addEventListener("scroll", handleScroll);return () => {window.removeEventListener("scroll", handleScroll);};}, []);

我们自定义一个hook封装一下。

import { useEffect, useState } from "react";
function useScrollPosition() {const [scrollX, setScrollX] = useState(0);const [scrollY, setScrollY] = useState(0);useEffect(() => {function handleScroll(event) {console.log(window.screenX, window.screenY);setScrollX(window.scrollX);setScrollY(window.scrollY);}window.addEventListener("scroll", handleScroll);return () => {window.removeEventListener("scroll", handleScroll);};}, []);return [scrollX, scrollY];
}export default useScrollPosition;

这样就可以在多个组件里面快速使用了。

import { memo, useEffect } from "react";
import "./style.css";
import useScrollPosition from "./hooks/useScrollPosition";const Home = memo((props) => {const [scrollX, scrollY] = useScrollPosition();return <div>Home:{scrollY} </div>;
});const About = memo((props) => {const [scrollX, scrollY] = useScrollPosition();return <div>About:{scrollY} </div>;
});const App = memo((props) => {const [scrollX, scrollY] = useScrollPosition();return (<div className="page">App:{scrollY}<Home /><About /></div>);
});
export default App;

style.css

.page {height: 2000px;
}

页面滚动的时候,多个组件都可以获取到坐标了。
在这里插入图片描述

storage

相关文章:

React基础知识四 Hooks

什么是hooks&#xff1f; (coderwhy) hooks是react 16.8&#xff08;2019年&#xff09;出的新特性。 react有两种形式来创建组件——类式和函数式。在hooks之前类式组件就是react最主流的编程方式。 这个时候&#xff0c;函数式组件是非常鸡肋的&#xff0c;几乎没什么用。因…...

线性代数中的谱分解

一、谱分解的基本原理 谱分解&#xff08;Spectral Decomposition&#xff09;是线性代数中的一个重要概念&#xff0c;特别是在研究矩阵的特征值和特征向量时。它指的是将一个矩阵分解为其特征值和特征向量的组合&#xff0c;从而简化矩阵的运算和分析。谱分解通常适用于对称…...

Redis 数据结结构(一)—字符串、哈希表、列表

Redis&#xff08;版本7.0&#xff09;的数据结构主要包括字符串&#xff08;String&#xff09;、哈希表&#xff08;Hash&#xff09;、列表&#xff08;List&#xff09;、集合&#xff08;Set&#xff09;、有序集合&#xff08;Sorted Set&#xff09;、超日志&#xff08…...

【Python】用Python和Paramiko实现远程服务器自动化管理

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在现代IT环境中,远程服务器管理已成为运维工作的常态。随着自动化运维的需求不断增加,如何高效地管理远程服务器,提升操作的灵活性和效率…...

PDF处理的创新工具:福昕低代码平台尝鲜

在当今数字化时代&#xff0c;PDF文件的处理和管理变得越来越重要。福昕低代码平台是新发布的一款创新的工具&#xff0c;旨在简化PDF处理和管理的流程。通过这个平台&#xff0c;用户可以通过简单的拖拽界面上的按钮&#xff0c;轻松完成对Cloud API的调用工作流&#xff0c;而…...

openstack创建浮动IP全过程

1、创建外部网络&#xff0c;即是provider网络&#xff0c;有关provider网络的详细解释请参见我之前的文章openstack中的self-service和provider网络_openstack provider网络不能创建vlan吗-CSDN博客 network create --share --external --provider-physical-network physnet1…...

nginx漏洞修复

漏洞名称&#xff1a;web服务器http信息头公开 解决&#xff0c;在以下各个监听端口加上一行&#xff0c;然后重启****nginx server_tokens off; 漏洞名称&#xff1a;默认的nginx http服务器设置 解决&#xff1a;请求头加上以下参数 add_header Content-Security-Policy “…...

Jackson - 序列化时更改字段名称

在这个简短的教程中&#xff0c;我将向您展示如何在序列化时更改字段名称以映射到另一个JSON属性。 Jackson库提供了JsonProperty注解&#xff0c;用于改变序列化JSON中的属性名称。 依赖项 首先&#xff0c;在pom.xml文件中添加以下依赖项&#xff1a; <dependency>…...

策略模式实战 - 猜拳游戏

**可以整体的替换一套算法&#xff0c;这就是策略模式。**这样对于同一个问题&#xff0c;可以有多种解决方案——算法实现的时候&#xff0c;可以通过策略模式来非常方便的进行算法的整体替换&#xff0c;而各种算法是独立封装好的&#xff0c;不用修改其内部逻辑。 具体的实…...

AWS ECS Task 添加 Prometheus 监控采集配置详细指南

以下是一篇完整的博文,介绍如何在 AWS ECS 环境中实现 JVM 监控。 AWS ECS 环境下的 JVM 监控实践 概述 在 AWS ECS (Elastic Container Service) 环境中监控 Java 应用性能是一项重要任务。本文将详细介绍如何使用 AWS Distro for OpenTelemetry (ADOT) 结合 Spring Boot …...

5. 一分钟读懂“工厂方法模式”

5.1 模式介绍 你可能会发现&#xff0c;简单工厂模式没在经典设计模式里出现&#xff0c;别急&#xff0c;它其实只是个常用的编程技巧&#xff0c;而不是标准的设计模式。简单工厂模式有三个要素&#xff1a;1个产品接口、n个产品类、1个工厂类&#xff0c;工厂类通过if/else来…...

基于 AutoFlow 快速搭建基于 TiDB 向量搜索的本地知识库问答机器人

导读 本文将详细介绍如何通过 PingCAP 开源项目 AutoFlow 实现快速搭建基于 TiDB 的本地知识库问答机器人。如果提前准备好 Docker、TiDB 环境&#xff0c;整个搭建过程估计在 10 分钟左右即可完成&#xff0c;无须开发任何代码。 文中使用一篇 TiDB 文档作为本地数据源作为示…...

C语言学习:速通指针(2)

这里要学习的有以下内容 1. const修饰指针 2. 野指针 3. assert断⾔ 4. 指针的使⽤和传址调⽤ 那么从这里开始 1. const 修饰指针 const修饰变量 首先我们知道变量是可以修改的&#xff0c;如果把变量的地址交给⼀个指针变量&#xff0c;通过指针变量的也可以修改这个变…...

windows 上ffmpeg编译好的版本选择

1. Gyan.dev Gyan.dev 是一个广受信赖的 FFmpeg 预编译库提供者&#xff0c;提供多种版本的 FFmpeg&#xff0c;包括静态和动态链接版本。 下载链接: https://www.gyan.dev/ffmpeg/builds/ 特点&#xff1a; 提供最新稳定版和开发版。 支持静态和共享&#xff08;动态&…...

Java设计模式笔记(二)

十四、模版方法模式 1、介绍 1&#xff09;模板方法模式(Template Method Pattern)&#xff0c;又叫模板模式(Template Patern)&#xff0c;在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需重写方法实现&#xff0c;但调用将以抽象类中定义的方式进行。 2&…...

Vue CLI的作用

Vue CLI&#xff08;Command Line Interface&#xff09;是一个基于Vue.js的官方脚手架工具&#xff0c;其主要作用是帮助开发者快速搭建Vue项目的基础结构和开发环境。以下是Vue CLI的具体作用&#xff1a; 1、项目模板与快速生成 Vue CLI提供了一系列预设的项目模板&#x…...

短视频矩阵系统开发|技术源代码部署

短视频矩阵系统通过多账号运营管理、多平台视频智能分发等功能&#xff0c;助力企业实现视频引流、粉丝沉淀和转化。 短视频矩阵系统是一种创新的营销工具&#xff0c;它整合了多账号管理、视频智能分发、数据可视化等多种功能&#xff0c;为企业在短视频领域的发展提供了强大…...

Erlang socket编程(二)

模拟服务器和客户端通信 %%%------------------------------------------------------------------- %%% author Administrator %%% copyright (C) 2024, <COMPANY> %%% doc %%% %%% end %%% Created : 03. 12月 2024 22:28 %%%---------------------------------------…...

工业检测基础-线扫相机和面阵相机参数及应用

以下是工业面阵相机和线扫相机的重要参数、应用场景以及调节方法的科普&#xff1a; 重要参数 分辨率&#xff1a; 面阵相机&#xff1a;由相机所采用的芯片分辨率决定&#xff0c;常用的有500万、1200万、6500万等像素&#xff0c;一般用长宽表示。如19201080等&#xff0c;…...

【无标题】建议用坚果云直接同步zotero,其他方法已经过时,容易出现bug

created: 2024-12-06T16:07:45 (UTC 08:00) tags: [] source: https://zotero-chinese.com/user-guide/sync author: 数据与文件的同步 | Zotero 中文社区 Excerpt Zotero 中文社区&#xff0c;Zotero 中文维护小组&#xff0c;Zotero 插件&#xff0c;Zotero 中文 CSL 样式 数…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

剑指offer20_链表中环的入口节点

链表中环的入口节点 给定一个链表&#xff0c;若其中包含环&#xff0c;则输出环的入口节点。 若其中不包含环&#xff0c;则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

html css js网页制作成品——HTML+CSS榴莲商城网页设计(4页)附源码

目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&#x1f…...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

短视频矩阵系统文案创作功能开发实践,定制化开发

在短视频行业迅猛发展的当下&#xff0c;企业和个人创作者为了扩大影响力、提升传播效果&#xff0c;纷纷采用短视频矩阵运营策略&#xff0c;同时管理多个平台、多个账号的内容发布。然而&#xff0c;频繁的文案创作需求让运营者疲于应对&#xff0c;如何高效产出高质量文案成…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...