从变量的角度理解 Hooks , 变得更简单了
从变量角度理解Hooks
在React的世界里,Hooks的引入为函数式组件带来了前所未有的灵活性和能力。它们让我们得以完全摆脱class
式的写法,在函数式组件中完成生命周期管理、状态管理、逻辑复用等几乎全部组件开发工作。这次,我们就从变量的角度来深入理解一下这些强大的Hooks。
一、useState:定义自变量
想象一下,我们有一个自变量x
,它代表组件的某个状态。在React中,我们可以使用useState
来定义这个自变量:
const [x, setX] = useState(0);
这里,x
就是我们的自变量,而setX
是一个函数,用于改变x
的值。
接下来,我们定义一个因变量y
,它是x
的函数:
const y = 2 * x + 1;
每次x
变化时,y
都会随之更新。
为了演示这个过程,我们可以创建一个简单的点击事件,每次点击时x
增加1:
export default function App() {const [x, setX] = useState(0);const y = 2 * x + 1;const changeX = () => setX(x + 1);return (<ul onClick={changeX}><li>x是{x}</li><li>y是{y}</li></ul>);
}
二、useMemo & useCallback:缓存因变量
在上面的例子中,每次组件重新渲染时,y
和changeX
都会重新计算。但在复杂的业务场景中,这些计算可能会变得非常昂贵。为了优化性能,我们可以使用useMemo
和useCallback
来缓存这些因变量。
useMemo
用于缓存一个数据类型的因变量:
//const y = 2 * x + 1;
const y = useMemo(() => 2 * x + 1, [x]);
而useCallback
则用于缓存一个函数类型的因变量:
//const changeX = () => setX(x + 1);
const changeX = useCallback(() => setX(x + 1), [x]);
这两个Hooks都接收一个创建函数和一个依赖项数组作为参数。只要依赖项不变,它们就会返回缓存的值或函数,从而避免不必要的计算。
三、useEffect:处理副作用
在函数式编程中,副作用是指一个函数在固定的输入下产生不固定的输出。在React中,副作用通常包括修改DOM、发起网络请求、设置定时器等。
为了处理这些副作用,我们可以使用useEffect
。它允许我们定义一个函数,在组件渲染后执行副作用逻辑。
例如,我们希望当x
变化时,将页面标题修改为x
的值:
useEffect(() => {document.title = x;
}, [x]);
这里,
useEffect
接收一个函数和一个依赖项数组。当依赖项变化时,函数会被执行。
四、useReducer:管理复杂状态
当组件的状态变得复杂时,我们可以使用useReducer
来管理它们。useReducer
可以看作是useState
的进阶版,它使用Redux的理念,将多个状态合并为一个状态对象,并通过一个reducer函数来更新状态。
- useReducer 和 redux 中 reducer 很像
- useState 内部就是靠 useReducer 来实现的
- useState 的替代方案,它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法
- 在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等
let initialState = 0;
// 如果你希望初始状态是一个{number:0}
// 可以在第三个参数中传递一个这样的函数 ()=>({number:initialState})
// 这个函数是一个惰性初始化函数,可以用来进行复杂的计算,然后返回最终的 initialState
const [state, dispatch] = useReducer(reducer, initialState, init);
这里,state
是我们的状态对象,dispatch
是一个函数,用于触发状态更新。
const initialState = 0;
function reducer(state, action) {switch (action.type) {case 'increment':return {number: state.number + 1};case 'decrement':return {number: state.number - 1};default:throw new Error();}
}
function init(initialState){return {number:initialState};
}
function Counter(){const [state, dispatch] = useReducer(reducer, initialState,init);return (<>Count: {state.number}<button onClick={() => dispatch({type: 'increment'})}>+</button><button onClick={() => dispatch({type: 'decrement'})}>-</button></>)
}
五、useContext: 跨组件层级传递自变量
useContext
允许你在组件树中跨多层级访问context(上下文)的值,而无需通过每层手动传递props。以下是对useContext的详细解释:
1、定义与功能
- 定义:useContext是一个React Hook,用于读取和订阅React组件中的context。
- 功能:它能够在组件树中共享数据,使得数据可以在不同层级的组件之间传递,而无需通过props逐层传递。
2、使用方式
- 创建Context:首先,你需要使用React.createContext()方法创建一个context对象。这个对象不保存数据,但它作为数据的载体,允许你从组件中获取或向组件提供数据。
- 提供数据:然后,你需要在组件树中使用Context.Provider包裹那些需要访问context的组件,并通过value属性向Provider提供数据。
- 读取数据:在需要访问context的组件中,你可以使用useContext Hook来读取context的值。你需要将先前创建的context对象作为参数传递给useContext。
3、示例
以下是一个简单的示例,展示了如何使用useContext在组件之间共享数据:
import React, { createContext, useContext, useState } from 'react';// 创建一个Context对象
const MyContext = createContext('defaultValue');function App() {// 使用useState创建一个state,并通过Provider将其提供给后代组件const [value, setValue] = useState('newValue');return (<MyContext.Provider value={value}><ChildComponent /><ButtonComponent /></MyContext.Provider>);
}function ChildComponent() {// 在子组件内使用useContext获取context的值const contextValue = useContext(MyContext);return <div>Child Component: {contextValue}</div>;
}function ButtonComponent() {// 假设这里有一个函数用于修改context的值(实际中可能需要通过某种方式触发这个函数)const handleClick = () => {// 这里需要一种方式来修改Provider中的value,通常是通过某种状态管理或回调函数实现// 例如,可以使用useReducer或其他状态管理库来更新value};return <button onClick={handleClick}>Change Context Value</button>;
}export default App;
注意:在这个示例中,ButtonComponent组件需要一种方式来修改Provider中的value。在实际应用中,这通常是通过某种状态管理(如useState、useReducer)或回调函数来实现的。由于这个示例是为了说明useContext的用法,所以并没有展示如何修改value的具体实现。
4、注意事项
(1) Provider嵌套:Provider组件可以嵌套使用,这样你就可以在不同的层级提供不同的context值。
(2) 性能优化:当context的值发生变化时,React会自动重新渲染那些读取了该context的组件。为了避免不必要的重渲染,你可以使用React.memo或shouldComponentUpdate等优化技术。
(3) 默认值:如果在组件树中没有找到对应的Provider,useContext会返回传递给createContext的defaultValue。
总的来说,useContext是React中一个非常有用的Hook,它能够帮助你在组件之间共享数据,从而简化组件间的通信和状态管理。
六、useRef:增加灵活性
最后,useRef
作为一个标记变量,提供了一种在组件的整个生命周期内持久存储数据的方法 , 并作用于自变量与因变量的不同路径中。它增加了组件逻辑的灵活性,允许我们在组件的生命周期内持久地存储数据。
以下是对useRef的详细解释:
1、定义与功能
- 定义:useRef是一个React Hook,它返回一个可变的ref对象,其
.current
属性被初始化为传递给useRef的参数(在组件的整个生命周期内保持不变)。 - 功能:useRef主要用于两个场景:访问DOM元素和保存跨渲染周期的变量。
2、使用场景
-
访问DOM元素:
- 当需要直接访问DOM元素(如设置焦点、测量尺寸等)时,可以使用useRef。
- 通过将ref对象传递给元素的
ref
属性,可以在组件中通过ref.current
访问该元素。
-
保存跨渲染周期的变量:
- useRef返回的ref对象在组件的整个生命周期内保持不变,因此可以用于存储不需要触发组件重新渲染的变量。
- 这使得useRef成为保存上一次渲染状态、在自定义Hook中共享数据等场景的理想选择。
3、示例
以下是一个使用useRef访问DOM元素和保存跨渲染周期变量的示例:
import React, { useRef } from 'react';function AutoFocusInput() {// 创建一个ref对象用于访问DOM元素const inputRef = useRef(null);// 处理按钮点击事件,使输入框获得焦点const handleButtonClick = () => {inputRef.current.focus();};// 创建一个ref对象用于保存跨渲染周期的变量const counterRef = useRef(0);// 增加计数器的值(不会触发组件重新渲染)const incrementCounter = () => {counterRef.current += 1;};return (<div><input type="text" ref={inputRef} /><button onClick={handleButtonClick}>聚焦输入框</button><p>计数器值:{counterRef.current}</p><button onClick={incrementCounter}>增加计数器</button></div>);
}export default AutoFocusInput;
在这个示例中,inputRef
用于访问输入框DOM元素,并在按钮点击时使其获得焦点。counterRef
用于保存一个计数器变量,并通过按钮点击事件增加其值。由于useRef返回的ref对象在组件的整个生命周期内保持不变,因此可以在多次渲染之间保持计数器的值。
4、注意事项
(1) 不要滥用useRef:虽然useRef可以在不触发组件重新渲染的情况下存储数据,但它不应该被用作主要的状态管理工具。对于需要响应状态变化的数据,应该使用useState或useReducer等状态管理Hook。
(2) 与class组件中的refs对比:在class组件中,refs是通过React.createRef()
创建的,并在componentDidMount
、componentDidUpdate
等生命周期方法中访问DOM元素。而在函数组件中,useRef提供了一种更简洁的方式来创建和使用refs。
总之,useRef是React中一个非常有用的Hook,它提供了在组件的整个生命周期内持久存储数据的方法,而不会触发组件的重新渲染。这使得它在访问DOM元素和保存跨渲染周期变量等场景中非常有用。
总结
从变量的角度来看,React Hooks的本质是自变量与因变量的关系。useState
用于定义自变量,useMemo
和useCallback
用于定义无副作用的因变量,useEffect
用于定义有副作用的因变量。为了方便操作更多的自变量,我们有了useReducer
;为了跨组件层级操作自变量,我们有了useContext
;最后,为了让组件逻辑更灵活,我们有了useRef
。
通过这些Hooks,我们可以在函数式组件中轻松实现复杂的逻辑和状态管理,享受React带来的高效和便捷。
相关文章:

从变量的角度理解 Hooks , 变得更简单了
从变量角度理解Hooks 在React的世界里,Hooks的引入为函数式组件带来了前所未有的灵活性和能力。它们让我们得以完全摆脱class式的写法,在函数式组件中完成生命周期管理、状态管理、逻辑复用等几乎全部组件开发工作。这次,我们就从变量的角度…...

LabVIEW Modbus通讯稳定性提升
在LabVIEW开发Modbus通讯程序时,通讯不稳定是一个常见问题,可能导致数据丢失、延迟或错误。为了确保通讯的可靠性,可以从多个角度进行优化,以下是一些有效的解决方案,结合实际案例进行分析。 1. 优化通讯参数设置 通讯…...

(8) cuda分析工具
文章目录 Nvidia GPU性能分析工具Nsight SystemNvidia GPU性能分析工具Nsight System Nvidia GPU性能分析工具Nsight System NVIDIA Nsight Systems是一个系统级的性能分析工具,用于分析和优化整个CUDA应用程序或系统的性能。它可以提供对应用程序整体性能的全面见…...

C语言 | Leetcode C语言题解之第517题超级洗衣机
题目: 题解: int findMinMoves(int* machines, int machinesSize){int sum0;for(int i0;i<machinesSize;i){summachines[i];}if(sum%machinesSize!0){return -1;}int psum/machinesSize;int ans0;int cur0;for(int i0;i<machinesSize;i){cur(mac…...

Java多线程编程基础
目录 编写第一个多线程程序 1. 方式一 : 继承Thread类, 重写run方法 2. 方式二: 实现Runnable接口, 重写run方法 3. 方式三: 使用Lambda表达式 [匿名内部类] [Lambda表达式] 在上个文章中, 我们了解了进程和线程的相关概念. 那么, 在Java中, 我们如何进行多线程编程呢? …...

刷代随有感(134):单调栈——下一个更大元素I(难点涉及哈希表与单调栈的结合)
单调栈处理的是下标! 题干: 代码: class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {stack<int>ddst;unordered_map<int,int>umap;vector<int…...

Linux云计算 |【第五阶段】CLOUD-DAY5
主要内容: 容器的镜像编排,commit简单镜像创建,Dockerfile制作服务镜像(语法、创建镜像)、创建复杂镜像(Docker微服务架构、示例:NGINXPHP)、私有仓库 一、简单镜像创建 1、自定义…...
被上传文件于后端的命名策略
上一篇博客我们了解了前端上传的文件资源应该存放在后端项目中的什么位置,那么随之而来的另一个问题——我们应该如何为上传的文件命名呢?往往直接采用原文件名并不稳妥,会导致命名冲突、文件冲突、数据库管理冲突等多种问题,下面…...
哈希表 算法专题
哈希表简介 是什么 存储数据的容器有啥用? "快速"查找某个元素什么时候用哈希表 频繁地查找某个数(有序用二分)怎么用哈希表 容器用数组模拟 字符串中的字符 范围比较小的数 一. 两数之和 两数之和 class Solution {public int[] twoSum(int[] nums, int targe…...
unity3d————[HideInInspector]
在Unity3D中,[HideInInspector]是一个属性修饰符,它的主要作用是在Unity的Inspector窗口中隐藏变量或属性。以下是关于[HideInInspector]的详细解释和作用: 作用 隐藏变量或属性:当你在脚本中使用[HideInInspector]修饰符时&…...
Soanrquber集成Gitlab 之 导入Gitlab项目
集成Gitlab 之 导入Gitlab项目 说明: Sonarquber里面的项目,顺便设置,只要在集成CI的时候,使用这个项目的项目标识即可。 当然项目名称一一对应是最好的了,所以这里讲导入Gitlab的项目,项目名称一一对应&…...
论区块链技术及应用
引言 区块链技术作为一种革命性的创新,近年来在全球范围内得到了广泛关注和应用。其去中心化、透明性和不可篡改的特性,使其在多个领域展现出了巨大的潜力。从金融到物联网,从医疗管理到政务监管,区块链正在改变我们处理信息和进…...

GPT避坑指南:如何辨别逆向、AZ、OpenAI官转
市面上有些说自己是官转,一刀只需要1块甚至几毛钱,并声称官方倍率的,很大可能就是使用的是 逆向或Azure。 如何鉴别逆向 逆向的种类很多,主要分为3类 逆向不知名A| 镜像站或偷的 key。成本约等于0,调用聊天数据可能在…...

Qt 文本文件读写与保存
Qt 文本文件读写与保存 开发工具:VS2013 QT5.8 设计UI界面,如下图所示 sample7_1QFile.h 头文件: #pragma once#include <QtWidgets/QMainWindow> #include "ui_sample7_1QFile.h"class sample7_1QFile : public QMainWin…...

Linux基础环境搭建(CentOS7)- 安装Scala和Spark
#Linux基础环境搭建(CentOS7)- 安装Scala和Spark Linux基础环境搭建(CentOS7)- 安装Scala和Spark 大家注意以下的环境搭建版本号,如果版本不匹配有可能出现问题!(spark不要下2.4版本的 会报错…...

SpringBoot 下的Excel文件损坏与内容乱码问题
序言 随着打包部署的方式的改变,原本正常运行的代码可能带来一些新的问题,比如我们现在使用SpringBoot 的方式生成Jar包直接运行,就会对我们再在Resource下的Excel文件产生影响,导入与预期不符的情况发生cuiyaonan2000163.com 比…...

官宣下代GPU存在缺陷,50系显卡或将迎来涨价
如果说 AMD 在 Ryzen 3000 系列还是和 intel 在 CPU 方面棋差一着的话,Ryzen 5000 系列就是打了个漂亮的翻身仗了。 凭借先进的 7nm 工艺制程和全新架构,让后来 intel 急忙推出「14nm」的 11 代酷睿也难以望其项背。 直到 intel 12 代发布的时候…...

使用pytorch实现LSTM预测交通流
原始数据: 免费可下载原始参考数据 预测结果图: 根据测试数据test_data的真实值real_flow,与模型根据测试数据得到的输出结果pre_flow 完整源码: #!/usr/bin/env python # _*_ coding: utf-8 _*_import pandas as pd import nu…...

C/C++(八)C++11
目录 一、C11的简介 二、万能引用与完美转发 1、万能引用:模板中的 && 引用 2、完美转发:保持万能引用左右值属性的解决方案 三、可变参数模板 1、可变参数模板的基本使用 2、push 系列和 emplace 系列的区别 四、lambda表达式…...

使用three.js 实现 自定义绘制平面的效果
使用three.js 实现 自定义绘制平面的效果 预览 import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.jsconst box document.getElementById(box)const scene new THREE.Scene()const camera new THREE.PerspectiveCam…...

Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...

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

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...