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

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

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

相关文章

电子病历静态数据脱敏路径探索

一、引言 数据脱敏&#xff08;Data Masking&#xff09;&#xff0c;屏蔽敏感数据&#xff0c;对某些敏感信息&#xff08;比如patient_name、ip_no、ad、no、icd11、drug等等 &#xff09;通过脱敏规则进行数据的变形&#xff0c;实现隐私数据的可靠保护。电子病历作为医疗领…

AIGC 012-Video LDM-更进一步,SD作者将LDM扩展到视频生成任务!

AIGC 012-Video LDM-Stable Video diffusion前身&#xff0c;将LDM扩展到视频生成任务&#xff01; 文章目录 0 论文工作1论文方法实验结果 0 论文工作 Video LDM作者也是Stable diffusion的作者&#xff0c;作者在SD的架构上进行扩展&#xff0c;实现了视频的生成。后续在Vid…

ABAP DIALOG屏幕编程1

一、DIALOG屏幕编程 DIALOG屏幕编程是SAP ABAP中用于创建用户交互界面的一种技术&#xff0c;主要用于开发事务性应用程序。它允许用户通过屏幕输入或操作数据&#xff0c;程序根据用户的操作执行逻辑处理。 1、DIALOG编程的主要组件 a、屏幕 (Screen) DIALOG程序的核心部分…

青训营-豆包MarsCode技术训练营试题解析二十七

介绍 ‌豆包青训营‌是由字节跳动和稀土掘金社区共同发起的技术培训和人才选拔项目。该项目的目标是培养具有职业竞争力的优秀开发工程师&#xff0c;并提供全程免费的课程&#xff0c;不收取任何费用‌。 课程内容和方向 豆包青训营的课程涵盖前端、后端和AI方向。在这个飞…

人工智能学习用的电脑安装cuda、torch、conda等软件,版本的选择以及多版本切换

接触人工智能的学习三个月了&#xff0c;每天与各种安装包作斗争&#xff0c;缺少依赖包、版本高了、版本低了、不兼容了、系统做一半从头再来了。。。这些都是常态。三个月把单位几台电脑折腾了不下几十次安装&#xff0c;是时候总结一下踩过的坑和积累的经验了。 以一个典型的…

华为关键词覆盖应用市场ASO优化覆盖技巧

在我国的消费者群体当中&#xff0c;华为的品牌形象较高&#xff0c;且产品质量过硬&#xff0c;因此用户基数也大。与此同时&#xff0c;随着影响力的增大&#xff0c;华为不断向外扩张&#xff0c;也逐渐成为了海外市场的香饽饽。作为开发者和运营者&#xff0c;我们要认识到…

Vue+Vite 组件开发的环境配置(超级简单)

vite是什么 Vite 是一个现代化的前端构建工具和开发服务器&#xff0c;它特别适用于构建大型的单页面应用&#xff08;SPA&#xff09;。Vite 旨在提供极快的冷启动速度&#xff0c;并且能够即时地&#xff08;几乎实时地&#xff09;更新模块&#xff0c;这得益于其利用原生 …

从仪表盘探索 MongoDB 关键指标

这是 MongoDB 监控系列文章的第七篇&#xff0c;前面几篇文章的链接如下&#xff1a; MongoDB 监控&#xff08;一&#xff09;MongoDB 监控&#xff08;二&#xff09;MongoDB 监控&#xff08;三&#xff09;MongoDB 监控&#xff08;四&#xff09;MongoDB 监控&#xff08…

mac安装php和xdebug调试

要在Mac上安装PHP 7.4&#xff0c;你可以通过几种方式来完成&#xff0c;但鉴于PHP7.4官方已不再维护&#xff0c;并且Homebrew默认仓库中不再提供此版本&#xff0c;我们需要从第三方仓库或直接从源代码进行安装。本文以brew方式安装&#xff0c;如果安装的是8.0以上&#xff…

UIlicious - 自动化端到端测试

在现代软件开发中&#xff0c;测试自动化已然成为产品交付质量的基石。而端到端测试&#xff08;E2E&#xff09;&#xff0c;作为验证整个应用流畅运行的关键&#xff0c;常常是测试工作中最具挑战性的一环。这时&#xff0c;一款简单高效的自动化测试工具——UIlicious&#…

新一代零样本无训练目标检测

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f916;编程探索专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年12月2日21点02分 神秘男子影, 秘而不宣藏。 泣意深不见, 男子自持重, 子夜独自沉。 论文链接 点击开启你的论文编程之旅h…

Python_Flask01

所有人都不许学Java了&#xff0c;都来学Python&#xff01; 如果不来学的话请网爆我的老师---蔡老师 Flask的前世姻缘 我不知道&#xff0c;没啥用&#xff0c;要学好这个框架&#xff0c;其实多读书&#xff0c;多看报就行了&#xff0c;真心想了解的话&#xff01; Welcom…

微信小程序版小米商城的搭建流程详解!

很多初学微信小程序语法的同学&#xff0c;可能不知道如何布局和搭建一个项目&#xff0c;下面我将讲解初学者如何搭建项目和注意事项。 目录 一、 app.json的配置 二、引入vant 三、主页banner携带参数跳转 四、点击商品项跳转详情页 一、 app.json的配置 {"pages&q…

Neo4j启动时指定JDK版本

项目使用jdk1.8&#xff0c;同时需要安装neo4j5.15版本&#xff0c;使用jdk17. 1.mac或者liunx&#xff0c;找到neo4j目录bin的下neo4j文件 设置JAVA_HOME: 2.windows,找到bin下面的neo4j.bat文件 set "JAVA_HOME{JDK文件目录}" 重启后生效。

AI大模型驱动数据分析:利用自然语言实现数据查询与可视化(1)

在当今AI驱动的时代&#xff0c;数据分析已成为各行各业不可或缺的能力。然而&#xff0c;传统的数据分析流程通常需要掌握SQL、数据处理和可视化等多项专业技能&#xff0c;这对非技术背景的业务人员来说是一个不小的挑战。 想象一下&#xff0c;当数据中心的负责人打开手机时…

汽车总线协议分析-FlexRay总线

随着汽车智能化发展&#xff0c;汽车增加安全性和舒适体验的功能增多&#xff0c;用于实现这些功能的传感器、ECU的数量也在持续上升&#xff0c;严重阻碍了线控技术的发展。常用的CAN、LIN等总线由于缺少同步性、确定性和容错性不能满足汽车线控系统(X-by-Wire)的要求。因此&a…

第二篇:k8s工作流程

我们来看通过deployment部署pod的常规流程&#xff1a; kubectl向apiserver发送部署请求&#xff08;例如使用 kubectl create -f deployment.yml&#xff09;apiserver将 Deployment 持久化到etcd&#xff1b;etcd与apiserver进行一次http通信。controller manager通过watch a…

SPC三种判定准则的算法

1.连续6个点递增或递减 //传入数据列表 //返回连续X个及以上递增或递减的数组下标int n = array.Length; int X = X_in; List<int> regions_start = new List<int>(); List<int> regions_end = new List<int>();if(Open){for (int i = 0; i < n - (…

工业—使用Flink处理Kafka中的数据_ProduceRecord1

1 、 使用 Flink 消费 Kafka 中 ProduceRecord 主题的数据,统计在已经检验的产品中,各设备每 5 分钟 生产产品总数,将结果存入Redis 中, key 值为

python爬虫01

前言 之前的学习不是很努力就间断更新了&#xff0c;现在所有的内容是在具有python基础和web基础上继续更新的。接下来是爬虫和Flask框架共同更新&#xff0c;一起加油吧。 接v&#xff1a;13053025350&#xff08;毕设&#xff0c;小程序&#xff09; 看不懂python基础的可以…