在React 16.8版本之前,组件主要分为两种:类组件(Class Components)函数组件(Function Components)。类组件可以使用 state 来管理内部状态,也能使用生命周期方法(如 componentDidMount)来处理副作用。而函数组件是“无状态的”,它们只能接收 props 并返回JSX,无法拥有自己的状态和生命周期。
React Hooks 是一系列特殊的函数,它们允许你在函数组件中“钩入”React 的 state 及生命周期等特性。 简单来说,Hooks 让函数组件也能拥有和类组件几乎同等的能力,从此你可以在不编写 class 的情况下使用 state 和其他 React 功能。这使得函数组件成为现代React开发的首选。

使用hooks的原则:

  • 只在顶层调用 Hooks:不要在循环、条件判断或嵌套函数中调用 Hooks。必须保证 Hooks 在每次组件渲染时的调用顺序都是完全一致的。这是因为 React 依赖于 Hooks 的调用顺序来正确地将 state 与对应的 useStateuseEffect 关联起来。
  • 只在 React 函数中调用 Hooks:你只能在 React 函数组件自定义 Hooks 中调用 Hooks。不能在普通的 JavaScript 函数中调用。

数据驱动更新型:

数据更新useState:

适用于管理组件的局部状态,如开关的开/关状态、表单输入的值、一个计数器的值等。当状态逻辑简单,且不依赖于其他复杂状态时,useState 是最佳选择。

表单:

import React, { useState } from 'react';function NameInput() {// 声明一个名为 name 的状态,初始值为空字符串const [name, setName] = useState('');const handleChange = (event) => {// 调用 setName 更新状态,触发重新渲染setName(event.target.value);};return (<div><input type="text" value={name} onChange={handleChange} /><p>Hello, {name}!</p></div>);
}
订阅更新useReducer:

当状态逻辑变得复杂,或者下一个状态依赖于前一个状态时,useReduceruseState 的一个更强大的替代方案。它借鉴了 Redux 的思想,通过 dispatch 一个 action 来集中管理状态的更新逻辑。
在事件处理中,调用 dispatch({ type: 'ACTION_TYPE', payload: ... }) 来触发状态更新。

import React, { useReducer } from 'react';// 1. Reducer 函数:定义所有可能的状态转换
const counterReducer = (state, action) => {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };case 'reset':return { count: 0 };default:throw new Error();}
};function Counter() {// 2. 使用 useReducer 初始化状态const [state, dispatch] = useReducer(counterReducer, { count: 0 });return (<div><p>Count: {state.count}</p>{/* 3. Dispatch actions 来触发更新 */}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button><button onClick={() => dispatch({ type: 'reset' })}>Reset</button></div>);
}

状态的获取和传递值:

订阅获取上下文useContext:

在 React 应用中,数据通常是通过 props 从父组件单向地流向子组件。但如果一个状态需要被深层次的子组件使用,或者被多个不同层级的组件共享,通过 props 层层传递(这个过程被称为 “prop drilling” 或“属性钻探”)会变得非常繁琐和难以维护。

  • 创建 Context: 使用 React.createContext() 创建一个 Context 对象。这个对象就像一个信息频道。
const MyContext = React.createContext(defaultValue);
  • 提供 Context: 在组件树的上层,使用 <MyContext.Provider> 组件,通过 value 属性来“广播”你想要共享的数据。所有被这个 Provider 包裹的子组件(无论层级多深)都能访问到这个 value
<MyContext.Provider value={/* 你想共享的任何值 */}><App />
</MyContext.Provider>
  • 消费 Context: 在任何一个子组件中,调用 useContext(MyContext) Hook 来“订阅”并读取这个 value
const value = useContext(MyContext);

全局状态管理:如应用的主题(白天/黑夜模式)、当前的登录用户信息、语言偏好设置等。

import React, { useState, useContext, createContext } from 'react';// 1. 创建一个主题 Context,可以给一个默认值
const ThemeContext = createContext('light');// App 组件作为顶层组件
function App() {const [theme, setTheme] = useState('light');const toggleTheme = () => {setTheme(current => (current === 'light' ? 'dark' : 'light'));};// 2. 使用 Provider 将 theme 和 toggleTheme 函数提供给所有子组件return (<ThemeContext.Provider value={{ theme, toggleTheme }}><Toolbar /></ThemeContext.Provider>);
}// Toolbar 组件,它本身不需要 theme,只是一个中间组件
function Toolbar() {return (<div><ThemedButton /></div>);
}// ThemedButton 是真正需要使用 theme 的深层子组件
function ThemedButton() {// 3. 使用 useContext 直接获取共享的主题和方法const { theme, toggleTheme } = useContext(ThemeContext);const style = {background: theme === 'dark' ? '#333' : '#eee',color: theme === 'dark' ? '#fff' : '#333',padding: '10px',border: 'none',borderRadius: '5px'};return (<button style={style} onClick={toggleTheme}>当前是 {theme} 主题,点我切换</button>);
}export default App;
元素组件获取useRef:

useRef 是一个非常独特的 Hook。虽然它也用于在组件中存储数据,但它与 useState 有一个本质区别:更新 useRef 的值不会触发组件的重新渲染

用途:
1.访问 DOM 元素
这是 useRef 的首要用途。你可以创建一个 ref,并将它附加到 JSX 元素的 ref 属性上,之后就可以通过这个 ref 直接访问该 DOM 节点。

import React, { useRef } from 'react';function FocusInput() {// 1. 创建一个 ref 对象const inputRef = useRef(null);const handleFocus = () => {// 3. 通过 .current 属性访问 DOM 节点并调用其方法if (inputRef.current) {inputRef.current.focus();}};return (<div>{/* 2. 将 ref 附加到 input 元素上 */}<input ref={inputRef} type="text" /><button onClick={handleFocus}>Focus the input</button></div>);
}

2.存储一个可变的引用值:

因为 useRef 的值在每次渲染时都保持不变,且更新它不会触发重渲染,所以它也可以作为一个“实例变量”,用来存储那些你需要在多次渲染之间共享、但又不想触发视图更新的数据。更新.current属性不会触发任何重渲染

import react, { useState, useEffect, useRef } from 'react';function RenderCounter() {const [count, setCount] = useState(0);// 使用 ref 来存储渲染次数const renderCount = useRef(0);useEffect(() => {// 每次渲染后,renderCount 的值加一// 注意:更新 ref 不会触发另一次渲染,避免了无限循环renderCount.current = renderCount.current + 1;});return (<div><p>State Count: {count}</p><p>This component has rendered {renderCount.current} times.</p>  <button onClick={() => setCount(c => c + 1)}>Trigger Re-render</button></div>);
}

状态派生和保存型:

  • 当你需要缓存一个计算结果(如一个经过过滤的数组,一个复杂的计算值)时,用 useMemo
  • 当你需要缓存一个函数本身(通常是为了作为 prop 传递)时,用 useCallback
派生新状态useMemo:

useMemo 的核心作用是 “记忆”一个计算结果。在 React 中,当一个组件的 stateprops 改变时,整个组件函数会重新执行,这意味着函数内部的所有代码(包括一些复杂的计算)都会被重新运行。如果某个计算非常耗时,这就会导致界面卡顿,影响用户体验。只有当其依赖项发生变化时,它才会重新执行计算,否则它会直接返回上一次缓存的结果。

适用场景:

  • 对一个巨大的列表进行排序或过滤。
  • 在组件中进行复杂的数学运算或数据处理。
  • 当一个子组件的 props 需要通过复杂计算得出时,用 useMemo 来稳定这个 prop,防止子组件不必要的重新渲染。
import React, { useState, useMemo } from 'react';// 假设 allUsers 是一个包含 1000 个对象的巨大数组
const allUsers = [...]; function UserList() {const [searchTerm, setSearchTerm] = useState('');const [anotherState, setAnotherState] = useState(false);//  没有优化的写法:// 无论 searchTerm 变不变,只要组件重渲染(比如点击 Toggle 按钮),// filter 这个昂贵操作就会被重新执行一次。// const filteredUsers = allUsers.filter(user => user.name.includes(searchTerm));//  使用 useMemo 优化:// 这个 filter 操作现在被“记忆”了。const filteredUsers = useMemo(() => {console.log('Filtering logic is running...'); // 你会发现只有在 searchTerm 改变时才会打印return allUsers.filter(user => user.name.includes(searchTerm));}, [searchTerm]); // 依赖项是 searchTermreturn (<div><input type="text" placeholder="Search users..." onChange={e => setSearchTerm(e.target.value)} />{/* 这个按钮的点击只会更新 anotherState,不会触发上面的 filter 计算 */}<button onClick={() => setAnotherState(!anotherState)}>Toggle</button><ul>{filteredUsers.map(user => <li key={user.id}>{user.name}</li>)}</ul></div>);
}
保存状态useCallback:

useCallback是什么?

  • useCallback 的核心作用是 “记忆”一个函数。在 JavaScript 中,函数是对象。在 React 组件每次重新渲染时,在函数组件内部定义的所有函数都会被重新创建。这意味着,即使函数体内的代码完全一样,前后两次渲染生成的函数在内存中也是两个不同的引用。
  • 当一个函数作为 prop 传递给一个被 React.memo 优化的子组件时,这个问题就变得很关键。因为父组件的每次重渲染都会创建一个新的函数实例,导致子组件接收到的 prop (那个函数) 每次都“不相等”,从而使得 React.memo 的优化失效,子组件依然会不必要地重新渲染。
  • useCallback 就是用来解决这个问题的。它会缓存你提供的函数实例,只有当其依赖项改变时,才会重新创建一个新的函数实例
import React, { useState, useCallback } from 'react';// 使用 React.memo 优化子组件,只有 props 变化时才重渲染
const MemoizedButton = React.memo(({ onClick, children }) => {console.log(`Button "${children}" is rendering...`);return <button onClick={onClick}>{children}</button>;
});function ParentComponent() {const [count, setCount] = useState(0);const [anotherState, setAnotherState] = useState(0);// 没有优化的写法:// 每次 ParentComponent 重渲染,都会创建一个新的 handleIncrement 函数。// const handleIncrement = () => setCount(count + 1);// 使用 useCallback 优化:// handleIncrement 函数被缓存了,它的引用只在依赖项变化时才更新。// 因为依赖项是空数组 [],所以它在组件的整个生命周期内都保持不变。const handleIncrement = useCallback(() => {setCount(prevCount => prevCount + 1); // 使用函数式更新,避免依赖 count}, []); // 这个函数依赖 anotherState,所以只有 anotherState 变化时才会重新创建const handleAnotherAction = useCallback(() => {// ... do something with anotherState}, [anotherState]);return (<div><p>Count: {count}</p><button onClick={() => setAnotherState(anotherState + 1)}>Update Another State (will not re-render Increment button)</button><MemoizedButton onClick={handleIncrement}>Increment Count</MemoizedButton><MemoizedButton onClick={handleAnotherAction}>Another Action</MemoizedButton></div>);
}

工具类:

服务端渲染: useId:

useId 是 React 18 中引入的一个新 Hook,它的主要目的是在客户端和服务端生成稳定且唯一的 ID,以解决服务端渲染(Server-Side Rendering, SSR)和客户端激活(Hydration)过程中的 ID 不匹配问题。
useId 出现之前,如果我们需要为组件生成一个唯一的 ID,生成一个在服务端和客户端之间稳定、唯一且无冲突的 ID 字符串。

import React, { useId } from 'react';function FormField() {// 调用 useId 生成一个在 SSR 和 CSR 中都稳定的唯一 IDconst id = useId();console.log('Generated ID:', id); // 在服务端和客户端会打印出相同的 ID,例如 ":r1:"return (<div>{/* 使用生成的 id 来关联 label 和 input */}<label htmlFor={id}>Your Name:</label><input id={id} type="text" name="name" /></div>);
}// 如果一个组件需要多个 ID
function ComplexFormField() {const id = useId();return (<div><label htmlFor={`${id}-firstName`}>First Name</label><input id={`${id}-firstName`} type="text" /><label htmlFor={`${id}-lastName`}>Last Name</label><input id={`${id}-lastName`} type="text" /></div>);
}

执行副作用型:

异步执行副作用useEffect:

这是处理副作用最常用、也是你最应该首先考虑的 Hook。useEffect 允许你在组件渲染到屏幕之后,执行一些与渲染本身无关的操作,比如数据获取、设置订阅、手动操作 DOM 等。“异步执行” 这个描述非常关键。useEffect 的执行时机是在 React 完成 DOM 更新并将其绘制到屏幕上之后。这意味着 useEffect 内部的代码不会阻塞浏览器的绘制过程,从而保证了用户界面的流畅和响应性。

执行流程:

  • React 渲染组件。
  • 浏览器更新 DOM 并且**绘制(Paint)**界面。
  • 然后useEffect 内部的函数被执行。
import React, { useState, useEffect } from 'react';function UserProfile({ userId }) {const [user, setUser] = useState(null);useEffect(() => {// 这个函数会在组件渲染到屏幕上之后被调用console.log('Component has been painted to the screen.');async function fetchUserData() {console.log('Starting data fetch...');const response = await fetch(`https://api.example.com/users/${userId}`);const userData = await response.json();setUser(userData);console.log('Data fetch complete.');}fetchUserData();// 清理函数:在组件卸载或下一次 effect 执行前运行return () => {console.log('Cleaning up previous effect.');};}, [userId]); // 依赖数组,仅在 userId 变化时重新执行if (!user) {return <div>Loading...</div>;}return <h1>{user.name}</h1>;
}
同步执行副作用: useLayoutEffect:

useLayoutEffect 在 API 上与 useEffect 完全相同,但它们的执行时机截然不同。这微小的差异导致了其用途的巨大区别。 “同步执行”useLayoutEffect 的关键。它会在 React 计算完所有 DOM 变更之后,但在浏览器将这些变更绘制到屏幕上之前同步执行。

执行流程:

  • React 渲染组件,并计算出 DOM 的变更。
  • 在浏览器绘制前useLayoutEffect 内部的函数被同步执行。
  • useLayoutEffect 内部的代码可能会再次触发状态更新,导致组件同步地重新渲染。
  • 然后,浏览器才将最终的 DOM 变更绘制到屏幕上。
    因为它是同步执行的,所以如果内部逻辑非常耗时,它会阻塞浏览器的绘制,导致页面卡顿。因此,应该谨慎使用。

只有当你需要在浏览器绘制前,读取 DOM 布局信息并同步地使用这些信息来改变 DOM 时,才应该使用 useLayoutEffect。这样做是为了防止用户看到“闪烁”(Flicker)现象——即组件先以一种状态渲染,然后又立即变为另一种状态。

import React, { useState, useLayoutEffect, useRef } from 'react';function AutoWidthInput() {const [width, setWidth] = useState(0);const divRef = useRef(null);// 使用 useLayoutEffect 来防止闪烁useLayoutEffect(() => {// 这个 effect 会在 DOM 更新后、浏览器绘制前执行if (divRef.current) {console.log('Reading layout before paint.');// 读取 div 的宽度const measuredWidth = divRef.current.offsetWidth;// 同步更新 statesetWidth(measuredWidth);}}, []); // 空数组表示只在挂载后执行一次// 如果这里用的是 useEffect,你可能会短暂地看到 input 宽度为 0,然后才跳到正确宽度,造成闪烁。return (<div><div ref={divRef} style={{ width: '200px', marginBottom: '10px', background: 'lightblue' }}>Measure my width!</div><input type="text" style={{ width: `${width}px` }} placeholder="My width matches the div" /></div>);
}

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

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

相关文章

【21】C# 窗体应用WinForm ——图片框PictureBox属性、方法、实例应用

文章目录12. 图片框PictureBox12.2 PictureBox插入、删除图片12.2.1 插入方式一&#xff1a;右键导入12.2.2 插入方式二&#xff1a;程序路径读入12.2.3 删除图片&#xff1a;右键清除12.3 实例&#xff1a;一键实现图片交换12.4 图片与窗口尺寸——SizeMode属性——实例对比1 …

Vue-Router 4.0:新一代前端路由管理

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

vuhub Corrosion2靶场攻略

靶场下载&#xff1a; 下载地址&#xff1a;https://download.vulnhub.com/corrosion/Corrosion2.ova 靶场使用&#xff1a; 我这里是使用Oracle VirtualBox虚拟机打开靶场&#xff0c;使用VMware打开攻击机kali&#xff0c;要使这两个机器能互相通信&#xff0c;需要将这两…

定制开发开源AI智能名片S2B2C商城小程序的特点、应用与发展研究

摘要&#xff1a;本文聚焦定制开发开源AI智能名片S2B2C商城小程序&#xff0c;深入剖析其技术特点、功能优势。通过分析在实体店与线上营销、新零售闭环生态构建、智慧场景赋能以及微商品牌规范化运营等方面的应用&#xff0c;探讨其发展趋势。旨在为营销技术专家中的营销创客及…

ulimit参数使用详细总结

目录 1. 基本介绍 1.1 核心功能 1.2 作用范围 1.3 限制类型 2. 基本语法 3. 常用选项​ 3.1 常见options 3.2 查看当前限制 4. 核心概念 4.1 软限制&#xff08;Soft Limit&#xff09; 4.2 硬限制&#xff08;Hard Limit&#xff09; 5. 修改限制 5.1 临时修改 …

基于ASIC架构的AI芯片:人工智能时代的算力引擎

基于ASIC架构的AI芯片&#xff1a;人工智能时代的算力引擎在深度学习模型参数量呈指数级增长、训练与推理需求爆炸式发展的今天&#xff0c;通用处理器&#xff08;CPU、GPU&#xff09;在能效比和计算密度上的局限日益凸显。基于ASIC&#xff08;Application-Specific Integra…

Linux信号机制:从硬件中断到用户态处理

当你在终端按下 CtrlC 时&#xff0c;一个简单的组合键触发了操作系统最精妙的异步通信机制。这种跨越硬件与软件的协作&#xff0c;正是Linux信号系统的精髓所在。本文将带你深入探索信号处理的全过程&#xff0c;从CPU中断到用户态函数调用&#xff0c;揭示Linux最强大的进程…

C语言基础:动态申请练习题

1. 动态申请一个具有10个float类型元素的内存空间&#xff0c;从一个已有的数组中拷贝数据&#xff0c;并找出第一次出现 12.35 的下标位置&#xff0c;并输出。#include <stdio.h> #include <stdlib.h> #include <string.h>int main() {// 动态申请10个flo…

MATLAB 实现 SRCNN 图像超分辨率重建

MATLAB 实现 SRCNN 图像超分辨率重建 MATLAB代码实现&#xff0c;用于基于三层卷积神经网络的图像超分辨率重建。代码参考了多个来源&#xff0c;结合了SRCNN的典型实现步骤。 1. MATLAB代码实现 % 超分辨率卷积神经网络(SRCNN)的测试代码 % 参考文献&#xff1a;Chao Dong, Ch…

知识蒸馏 - 基于KL散度的知识蒸馏 HelloWorld 示例

知识蒸馏 - 基于KL散度的知识蒸馏 HelloWorld 示例 flyfish 知识蒸馏 - 蒸的什么 知识蒸馏 - 通过引入温度参数T调整 Softmax 的输出 知识蒸馏 - 对数函数的单调性 知识蒸馏 - 信息量的公式为什么是对数 知识蒸馏 - 根据真实事件的真实概率分布对其进行编码 知识蒸馏 - …

从结构到交互:HTML5进阶开发全解析——语义化标签、Canvas绘图与表单设计实战

一、语义化标签进阶&#xff1a;重构页面结构的「逻辑语言」 在 HTML5 的舞台上&#xff0c;语义化标签是熠熠生辉的主角&#xff0c;它们为网页赋予了清晰的逻辑结构&#xff0c;使其更易被搜索引擎理解和被开发者维护。其中&#xff0c;<section>与<article>标签…

标准七层网络协议和TCP/IP四层协议的区别

分别是什么? OSI七层协议是国际标准组织制定的标准协议。其中七层分别是物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。 TCP/IP协议是美国军方在后期网络技术的发展中提出来的符合目前现状的协议。其中四层分别是网络接口层对应七层中的物理层和数据链路层,…

前端面试手撕题目全解析

以下是前端面试中常遭遇的“手撕”基础题目汇总&#xff0c;涵盖 HTML→JS→Vue→React&#xff0c;每题附经典实现&#xff0f;原理解析&#xff0c;可现场答题或后端总结。 HTML 基础题 &#x1f4dd; 语义化卡片&#xff08;Semantic Card ARIA&#xff09; <article cl…

道格拉斯-普克算法 - 把一堆复杂的线条变得简单,同时尽量保持原来的样子

道格拉斯-普克算法 - 把一堆复杂的线条变得简单&#xff0c;同时尽量保持原来的样子 flyfish 道格拉斯-普克算法&#xff08;Douglas-Peucker Algorithm解决的问题其实很日常&#xff1a;把一堆复杂的线条&#xff08;比如地图上的道路、河流&#xff0c;或者GPS记录的轨迹&…

团购商城 app 系统架构分析

一、引言 团购商城 APP 作为一种融合了电子商务与团购模式的应用程序&#xff0c;近年来在市场上取得了显著的发展。它为用户提供了便捷的购物体验&#xff0c;同时也为商家创造了更多的销售机会。一个完善且高效的系统架构是保障团购商城 APP 稳定运行、提供优质服务的基础。本…

【AI平台】n8n入门7:本地n8n更新

✅0、前言 目标&#xff1a;本地n8n部署后&#xff0c;有新版本&#xff0c;然后进行更新。官方文档&#xff1a;Docker | n8n Docs特别说明&#xff1a; n8n镜像更新后&#xff0c;容器重建&#xff0c;所以之前在n8n配置的东西&#xff0c;就莫有了&#xff0c;工作流提前导…

还在使用Milvus向量库?2025-AI智能体选型架构防坑指南

前言说明&#xff1a;数据来源&#xff1a;主要基于 Milvus&#xff08;v2.3&#xff09;和 Qdrant&#xff08;v1.8&#xff09;的最新稳定版&#xff0c;参考官方文档、GitHub Issues、CNCF报告、以及第三方评测&#xff08;如DB-Engines、TechEmpower&#xff09;。评估原则…

3-verilog的使用-1

verilog的使用-1 1.判断上升沿 reg s_d0; reg s_d1; wire signal_up ; //判断信号的上升沿 assign signal_up (~touch_key_d1) & touch_key_d0; always (posedge clk or negedge rst_n) beginif(rst_n 1b0) begins_d0< 1b0;s_d1< 1b0;endelse begins_d0&…

ESXI虚拟交换机 + H3C S5120交换机 + GR5200路由器组网笔记

文章目录一、组网拓扑与核心逻辑1. 拓扑结构2. 核心逻辑二、详细规划方案1. VLAN 与 IP 地址规划2. 设备连接规划三、配置步骤1. H3C S5120 交换机配置&#xff08;VLAN 与端口&#xff09;2. H3C GR5200 路由器配置&#xff08;路由、网关、NAT&#xff09;3. ESXi 虚拟交换机…

python的驾校培训预约管理系统

前端开发框架:vue.js 数据库 mysql 版本不限 后端语言框架支持&#xff1a; 1 java(SSM/springboot)-idea/eclipse 2.NodejsVue.js -vscode 3.python(flask/django)–pycharm/vscode 4.php(thinkphp/laravel)-hbuilderx 数据库工具&#xff1a;Navicat/SQLyog等都可以 该系统通…