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 面试问题!
当面试官问你:
useReducer
、useCallback
、useMemo
、useRef
这几个 React Hooks 都有什么作用?他其实是在考察你对 React 中几个重要但相对进阶的 Hooks 的理解与实际应用场景,尤其是它们解决了什么问题、什么时候该用、和其它 Hook(比如
useState
、useEffect
)有何区别。下面我会逐个为你详细讲解这 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
包裹的(避免子组件无意义重渲染)函数在 依赖项不变的情况下需要保持引用稳定(比如用在
useEffect
、useMemo
或一些库中要求函数引用稳定时)
🧠 面试回答关键词:
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 的繁琐。
使用步骤通常是:
创建 Context: 用
React.createContext(defaultValue)
创建一个上下文对象;提供数据: 在组件树的上层用
<Context.Provider value={data}>
包裹子组件,传递共享的数据;消费数据: 在任意子组件(无论多深)中,通过
const value = useContext(Context)
直接获取到对应的 Context 数据。
它特别适合用于全局状态或数据的共享,比如主题、用户信息、语言设置、全局配置等场景,能够极大提升代码的可维护性和开发效率。
✅ useContext 的做法:跨层级直接获取共享数据
使用 Context
,你只需要:
在最上层用 Provider 包裹,提供数据
在任意子组件(无论多深)用 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 |
| 同左 |
提供数据 |
| 同左 |
消费数据 | 1. | |
useContext 的典型使用场景
场景 | 说明 |
---|---|
主题切换(Dark Mode / Light Mode) | 全局主题数据,任意组件都可获取或响应变化 |
用户登录状态(User Info / Token) | 比如当前用户信息,可以在导航栏、个人中心等任何地方使用 |
多语言国际化(i18n) | 当前语言、翻译文本,可以在任何子组件中获取 |
全局配置(如 API 地址、功能开关) | 不需要层层传递的配置项 |
全局状态管理(简单场景) | 在引入 Redux/Zustand/Jotai 之前,可以用 Context + |
⚠️ 注意事项
注意点 | 说明 |
---|---|
Context 的更新会触发所有消费该 Context 的组件重新渲染 | 如果 Context 的 value 变化,所有用 |
优化方案 | 如果 Context 的数据很庞大或更新频繁,可以考虑拆分成多个 Context,或者配合 |
默认值 |
|
如何用
useContext + useReducer
模拟一个小型的全局状态管理(类似 Redux 简化版)
什么情况下不需要用 useCallback
?
如果你的函数没有作为 props 传递给子组件,或者子组件没有做优化(比如没有用 React.memo
),或者函数引用变化不会带来性能问题,那么通常就不需要用 useCallback
。
useRef
和 useState 保存变量有什么区别?
useState
用于保存会触发组件重新渲染的 状态数据,而 useRef
用于保存不会触发渲染的 可变变量(比如定时器ID、DOM引用、上一次的值等)。
自定义 Hook 和这些 Hook 如何配合使用?
自定义 Hook 可以和所有内置的 React Hooks 配合使用,因为自定义 Hook 本质上就是一个普通函数,它也可以调用如 useState
、useEffect
、useReducer
、useCallback
、useMemo
、useRef
、useContext
等 Hooks。
通过将多个内置 Hook 组合封装进一个自定义 Hook,我们可以实现逻辑的复用,比如封装状态管理、副作用处理、数据请求、DOM 操作、全局状态读取等。这样可以让组件更加简洁,逻辑更加清晰,同时提高代码的可维护性和复用性。
例如,我们可以用 useReducer
+ 自定义 Hook 封装复杂状态逻辑,用 useRef
+ useEffect
封装 DOM 操作或保存上一次的值,用 useContext
+ 自定义 Hook 简化全局数据的读取,等等。
eg:
场景 2:useRef
+ useEffect
→ 封装 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>
);
}