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

react常用的hooks

“这四个 Hook 分别解决不同场景下的问题:

  • useReducer​ 用于管理复杂的状态逻辑,特别是当 state 是一个对象或需要根据 action 来更新时,它比 useState更适合有复杂更新逻辑的场景;

  • useCallback​ 用于缓存函数,避免每次渲染都创建新的函数实例,主要用于将函数作为 props 传递给子组件时避免不必要的子组件重渲染,常与 React.memo配合使用;

  • useMemo​ 用于缓存计算结果,避免重复计算开销较大的值,只有依赖项变化时才重新计算,常用于性能优化;

  • useRef​ 用于保存可变值且不触发重新渲染,比如保存定时器 ID、上一次的值,或者直接访问和操作 DOM 元素。

它们都是为了更高效、更可控地管理状态、逻辑和副作用,根据不同的场景选择合适的 Hook 能显著提升代码的可维护性与性能。”

  • useCallback是缓存函数,适用于将函数作为 props 传递给子组件,防止因函数引用改变导致子组件无意义的重新渲染,通常配合 React.memo使用​

  • useMemo是缓存值,适用于缓存计算成本高的结果,或者保证某些引用类型(如对象、数组)在多次渲染中保持不变​

  • ​两者都是依赖项不变时才返回缓存,依赖变了才会重新计算/创建​

  • useCallback(fn, deps)相当于 useMemo(() => fn, deps),但前者返回函数,后者返回任意值​

react的hook有哪些


⚙️ ​​基本用法:​

const [state, dispatch] = useReducer(reducer, initialState);


✅ ​​示例:​

const initialState = { count: 0 };function reducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:return state;}
}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<div>Count: {state.count}<button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}

二、useCallback

  • useReducer
  • 这是一个非常经典且高频的 ​​React Hooks 面试问题​​!

    当面试官问你:

    useReduceruseCallbackuseMemouseRef这几个 React Hooks 都有什么作用?​

    他其实是在考察你对 ​​React 中几个重要但相对进阶的 Hooks 的理解与实际应用场景​​,尤其是它们解决了什么问题、什么时候该用、和其它 Hook(比如 useStateuseEffect)有何区别。


    下面我会逐个为你详细讲解这 4 个 Hook 的作用、使用场景、原理(简要)、以及 ​​面试回答技巧​​,让你不仅会用,还能清晰表达出来,给面试官留下好印象 ✅


    一、useReducer

  • useReducer用于​​管理包含多个子状态、或者状态逻辑较复杂的情况​​。管理复杂的 state 逻辑,特别是当 state 是一个对象或需要根据 action 来更新时。它接收一个 reducer 函数和初始值,返回当前 state 和一个 dispatch 方法。它比 useState更适合有复杂状态更新逻辑的场景,也可以更好地组织代码,便于维护。

    它特别适合以下情况:

  • 当前组件的 ​​state 比较复杂,不是一个简单值,而是一个对象或嵌套结构​

  • state 的 ​​更新逻辑比较复杂,可能涉及多个子状态的修改​

  • 你希望把 ​​状态更新的逻辑集中管理(比如抽离到 reducer 函数中)​

  • 你想实现更类似于 Redux 的状态更新模式(但不用引入 Redux)

  • reducer:一个函数,接收当前的 state 和一个 action,返回新的 state(类似 Redux 的 reducer)

  • initialState:初始状态

  • 返回值:

    • state:当前状态

    • dispatch:一个函数,用于派发 action,触发状态更新

useCallback用于缓存函数引用,避免在每次组件渲染时都生成新的函数实例,从而优化性能。它接收一个函数和依赖项数组,只有依赖项发生变化时才会重新创建函数。常用于将函数作为 props 传递给子组件,防止因函数引用变化导致子组件不必要的重新渲染,特别是配合 React.memo使用时


⚙️ ​​基本用法:​

const memoizedCallback = useCallback(() => {doSomething(a, b);
}, [a, b]); // 依赖项


✅ ​​使用场景:​



三、useMemo

  • 返回一个 ​​记忆化的回调函数​

  • 只有当依赖项 [a, b]发生变化时,才会重新创建这个函数

  • 否则,多次渲染返回的是同一个函数引用

  • 将函数作为 ​​props 传递给子组件,且子组件是 React.memo包裹的(避免子组件无意义重渲染)​

  • 函数在 ​​依赖项不变的情况下需要保持引用稳定​​(比如用在 useEffectuseMemo或一些库中要求函数引用稳定时)

🧠 ​​面试回答关键词:​

useMemo用于缓存计算结果,避免在每次渲染时都重新计算开销较大的值。它接收一个函数和一个依赖项数组,只有依赖项发生变化时才会重新计算并返回新值,否则返回之前缓存的结果。常用于优化性能,比如避免重复计算昂贵操作,或保证传递给子组件的引用类型 props(如对象、数组)保持稳定。

它和 useCallback很像,但:


⚙️ ​​基本用法:​

const memoizedValue = useMemo(() => {return computeExpensiveValue(a, b);
}, [a, b]);


✅ ​​使用场景:​

⚠️ 注意:​​不要滥用!只有真正遇到性能瓶颈时才使用,大多数情况下 React 的渲染性能已经足够好。​

四、useRef

useRef用于保存一个可变的值,其值在组件的整个生命周期中保持不变,且修改它不会触发组件重新渲染。它常用于两种场景:一是访问和操作 DOM 元素(比如聚焦 input),二是保存一些不需要触发渲染的变量(比如定时器 ID、上一次的值等)。返回的 ref 对象通过 .current属性来存取值。


⚙️ ​​基本用法:​

const refContainer = useRef(initialValue);


✅ ​​常见使用场景:​

1. ​​访问 DOM 元素​
const inputRef = useRef(null);const focusInput = () => {inputRef.current.focus();
};return <input ref={inputRef} />;
2. ​​保存上一次的值(比如上一次的 props 或 state)​
const prevCountRef = useRef();useEffect(() => {prevCountRef.current = count;
}, [count]);const prevCount = prevCountRef.current; // 上一次的 count
3. ​​保存定时器 ID、滚动位置等,且不触发重新渲染​

  • useCallback缓存的是 ​​函数​

  • useMemo缓存的是 ​​计算结果(一个值)​

  • 返回一个 ​​记忆化的值​

  • 只有依赖项 [a, b]改变时,才会重新计算

  • 否则直接返回上一次缓存的结果

  • 有一个 ​​计算成本较高的值(比如过滤列表、复杂计算等)​​,你不希望每次渲染都重新计算

  • 依赖项不变时,返回缓存的值,避免重复计算

  • 有时也用于 ​​性能优化,确保传递给子组件的 props 是稳定的引用(比如对象、数组)​

  • refContainer.current用来存取值

  • 修改 ref.current​不会触发组件重新渲染​

  • 返回的 ref 对象在组件的整个生命周期内保持不变

五.useState:管理函数组件中的状态

state声明方式:在函数组件中通过 useState 直接获取,类组件通过constructor 构造函数中设置。

state读取方式:在函数组件中直接使用变量,类组件通过this.state.count的方式获取

state更新方式:在函数组件中通过 setCount 更新,类组件通过this.setState()

总的来讲,useState 使用起来更为简洁,减少了this指向不明确的情况

六.useEffect:

在函数组件中进行一些带有副作用的操作,可以模拟类组件的生命周期

【】:模拟compopnentDidMount 组件挂载时候执行

不传:componentDidUpdate 每次渲染都执行

【id】:componentDidUpdate 

返回清理函数:则是组件卸载时候了componentWillUnmount

hooks能够更容易解决状态相关的重用的问题:

每调用useHook一次都会生成一份独立的状态

通过自定义hook能够更好的封装我们的功能


七、useContext

useContext是 React 提供的一个 Hook,用于在函数组件中获取由 React.createContext创建的 Context 的值。它的主要作用是实现跨组件、跨层级的状态共享,避免了传统 逐层手动传递 props 的繁琐。​

​使用步骤通常是:​

  1. ​创建 Context:​​ 用 React.createContext(defaultValue)创建一个上下文对象;

  2. ​提供数据:​​ 在组件树的上层用 <Context.Provider value={data}>包裹子组件,传递共享的数据;

  3. ​消费数据:​​ 在任意子组件(无论多深)中,通过 const value = useContext(Context)直接获取到对应的 Context 数据。

​它特别适合用于全局状态或数据的共享,比如主题、用户信息、语言设置、全局配置等场景,能够极大提升代码的可维护性和开发效率。​


✅ useContext 的做法:跨层级直接获取共享数据

使用 Context,你只需要:

  1. ​在最上层用 Provider 包裹,提供数据​

  2. ​在任意子组件(无论多深)用 useContext 获取数据​

👉 ​​无需手动逐层传递 props!​

useContext 的基本用法(代码示例)

1. 创建一个 Context

// 1. 创建 Context(通常在单独文件中,比如 ThemeContext.js)
import React from 'react';const ThemeContext = React.createContext('light'); // 默认值是 'light'
export default ThemeContext;

2. 在顶层组件用 Provider 提供数据

// App.js
import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import ChildComponent from './ChildComponent';function App() {const [theme, setTheme] = useState('dark');return (// 2. 用 Provider 包裹子树,传递 value<ThemeContext.Provider value={theme}><div><h1>当前主题是:{theme}</h1><ChildComponent /></div></ThemeContext.Provider>);
}

3. 在任意深层子组件中用 useContext 获取数据 ✅

// ChildComponent.js
import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';function ChildComponent() {// 3. 直接使用 useContext 获取 ThemeContext 的值const currentTheme = useContext(ThemeContext);return <p>子组件获取到的主题是:{currentTheme}</p>;
}

🔥 ​​注意:ChildComponent 可能在组件树的任意层级,不需要手动传递 theme!​

类组件 vs 函数组件中的 Context

类组件

函数组件

创建 Context

React.createContext()

同左

提供数据

<Context.Provider value={...}>

同左

消费数据

1. static contextType = Contextthis.context
2. 或 <Context.Consumer>

useContext(Context)​(推荐,简单直观)


useContext 的典型使用场景

场景

说明

​主题切换(Dark Mode / Light Mode)​

全局主题数据,任意组件都可获取或响应变化

​用户登录状态(User Info / Token)​

比如当前用户信息,可以在导航栏、个人中心等任何地方使用

​多语言国际化(i18n)​

当前语言、翻译文本,可以在任何子组件中获取

​全局配置(如 API 地址、功能开关)​

不需要层层传递的配置项

​全局状态管理(简单场景)​

在引入 Redux/Zustand/Jotai 之前,可以用 Context + useContextuseState/useReducer做轻量级全局状态共享


⚠️ 注意事项

注意点

说明

​Context 的更新会触发所有消费该 Context 的组件重新渲染​

如果 Context 的 value 变化,所有用 useContext获取该 Context 的组件都会重新渲染,可能会影响性能

​优化方案​

如果 Context 的数据很庞大或更新频繁,可以考虑拆分成多个 Context,或者配合 React.memouseMemo等优化手段

​默认值​

React.createContext(defaultValue)的 defaultValue 只在 ​​没有匹配到 Provider 的情况下生效​​,一般用于开发环境或 fallback

  • 如何用 useContext + useReducer模拟一个小型的全局状态管理(类似 Redux 简化版)

什么情况下不需要用 useCallback

如果你的函数没有作为 props 传递给子组件,或者子组件没有做优化(比如没有用 React.memo),或者函数引用变化不会带来性能问题,那么通常就不需要用 useCallback。​

useRef和 useState 保存变量有什么区别?

useState用于保存会触发组件重新渲染的​​ ​​状态数据​​,而 ​useRef用于保存不会触发渲染的​​ ​​可变变量(比如定时器ID、DOM引用、上一次的值等)​​。

自定义 Hook 和这些 Hook 如何配合使用?

   自定义 Hook 可以和所有内置的 React Hooks 配合使用,因为自定义 Hook 本质上就是一个普通函数,它也可以调用如 useStateuseEffectuseReduceruseCallbackuseMemouseRefuseContext等 Hooks。​

​通过将多个内置 Hook 组合封装进一个自定义 Hook,我们可以实现逻辑的复用,比如封装状态管理、副作用处理、数据请求、DOM 操作、全局状态读取等。这样可以让组件更加简洁,逻辑更加清晰,同时提高代码的可维护性和复用性。​

​例如,我们可以用 useReducer+ 自定义 Hook 封装复杂状态逻辑,用 useRefuseEffect封装 DOM 操作或保存上一次的值,用 useContext+ 自定义 Hook 简化全局数据的读取,等等。​

eg:

场景 2:useRefuseEffect→ 封装 DOM 操作或保存上一次的值

你希望在组件中保存上一次的 props 或 state 值,用于比较或展示。

解决方案:

用 useRef保存上一次的值,配合 useEffect在每次更新时保存当前值

// usePrevious.js
import { useEffect, useRef } from 'react';

export function usePrevious(value) {
const ref = useRef();

  useEffect(() => {
ref.current = value; // 在 effect 中更新 ref,确保拿到的是上一次渲染的值
}, [value]);

  return ref.current; // 返回上一次的值
}

组件中使用

function MyComponent({ count }) {
const prevCount = usePrevious(count);

  return (
<div>
<p>当前 count: {count}</p>
<p>上一次的 count: {prevCount}</p>
</div>
);
}

http://www.dtcms.com/a/392491.html

相关文章:

  • 重构的艺术:从‘屎山’恐惧到优雅掌控的理性之旅
  • 在c++中,怎么理解把析构函数设置为virtual呢?
  • CUDA性能优化 ---- 通过矢量化内存访问提高性能
  • 【序列晋升】39 Spring Data REST 的优雅实践,让数据交互更符合 REST 规范
  • 能当关系型数据库还能玩对象特性,能拆复杂查询还能自动管库存,PostgreSQL 凭什么这么香?
  • 【2025PolarCTF秋季个人赛】WEB方向wp
  • Go基础:Go语言函数和方法详解
  • Redis 遍历指定格式的所有key
  • 插入mathtype/latex公式在word中行间距变高了
  • 设计模式学习(四)代理模式、适配器模式
  • ​​[硬件电路-279]:DRV8818PWP功能概述、管脚定义
  • 【51单片机】【protues仿真】基于51单片机恒温箱系统
  • zk管理kafka有哪些不足
  • Java 大视界 -- Java 大数据机器学习模型在金融衍生品复杂风险建模与评估中的应用
  • 半导体制造中常见工艺之LPCVD
  • D01粉尘传感器详解(STM32)
  • 【小程序】微信小程序页面之间数据传递的五种方法
  • Taichi太极图形编程语言实践demo
  • [xboard]07-Makefile逐行分析1
  • 基于规则的专家系统对自然语言处理深层语义分析的影响与启示:历史演进、技术局限与未来融合路径
  • 鸿蒙分布式服务架构实战:从服务注册到远程调用的完整指南
  • PPT中设置和应用空白版式,和占位符干扰说再见
  • Elasticsearch 02
  • 283-基于Django的AppStore应用榜单数据可视化分析推荐系统
  • 星际漫游2025“∞无限”潮玩艺术周于顺德启幕,以东方符号重构潮流宇宙
  • Rust:重塑系统编程的未来,从安全到性能的技术革命
  • Vue 3 提供的 createElement 工具函数——h
  • 在开放系统互联参考模型(OSI)中,安全服务有哪些
  • 鸿蒙分布式文件操作实际开发案例
  • effect的参数和返回值