以下是整理的 20 道 React 面试题,涵盖基础、进阶和实战应用,适用于社招或内推准备:
一、React 基础(适合初中级)
-
React 中的组件有哪几种?它们有什么区别?
-
什么是 JSX?它与 HTML 有什么不同?
-
React 是如何实现虚拟 DOM 的?它的优势是什么?
-
组件的生命周期函数有哪些?React 18 中的变化是?
-
React 中 key 的作用是什么?为什么不能用 index 作为 key?
-
如何在函数组件中模拟 componentDidMount?
-
受控组件和非受控组件的区别?分别适用于什么场景?
-
React 中事件绑定有哪些方式?有什么坑需要注意?
-
如何实现组件之间的通信?
-
React 如何实现条件渲染和列表渲染?
二、Hooks 专题(React 16.8+)
-
useEffect 有哪些使用场景?依赖数组写错会带来什么问题?
-
useRef 和 useState 的区别?分别适用于哪些场景?
-
React 中如何避免 useEffect 死循环?
-
如何实现一个自定义 Hook?能举个例子吗?
-
为什么不能在循环、条件或嵌套函数中调用 Hook?
三、性能优化与实战技巧
-
React 中如何避免不必要的重新渲染?有哪些优化手段?
-
React.memo、useMemo 和 useCallback 的作用与区别?
-
React 中如何实现懒加载?如何配合 Suspense 使用?
-
如何在 React 中做错误边界处理?为什么函数组件不能做?
-
React18 中 Concurrent Mode 是什么?对开发者有什么影响?
好的,下面是前 5 道 React 面试题,以八股文风格整理,方便记忆和背诵:
- React 中的组件有哪几种?它们有什么区别?
答:
React 组件主要分为两类:类组件(Class Component)与函数组件(Function Component)。
类组件通过 class 关键字定义,需继承 React.Component,具备生命周期函数、this.state 和 this.setState 等特性;
函数组件最初为纯展示组件,自 React 16.8 引入 Hook 后也能拥有状态与副作用逻辑,写法更简洁。
当前推荐使用函数组件,因其更易组合复用,性能优化更简单,官方也逐步弃用类组件。
- 什么是 JSX?它与 HTML 有什么不同?
答:
JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中书写类似 HTML 的代码,用于描述 UI 结构。
JSX 本质是 React.createElement 的语法糖,最终会被编译为 JavaScript 对象。
与 HTML 的不同点包括:
所有标签必须闭合;
属性使用驼峰命名,如 className、onClick;
表达式需用 {} 包裹;
可以嵌套任意 JS 表达式。
- React 是如何实现虚拟 DOM 的?它的优势是什么?
答:
React 中的虚拟 DOM 是用 JavaScript 对象描述真实 DOM 结构的中间表示。每次状态变化后,会生成新的虚拟 DOM 树,并与旧的树进行 Diff 运算,找出差异部分,再最小化更新真实 DOM。
其优势包括:
避免直接操作真实 DOM,提高性能;
更好的跨平台能力;
有利于实现时间切片、异步渲染等高级特性。
- 组件的生命周期函数有哪些?React 18 中的变化是?
答:
类组件的生命周期分为三个阶段:
挂载阶段:constructor → render → componentDidMount;
更新阶段:shouldComponentUpdate → render → componentDidUpdate;
卸载阶段:componentWillUnmount。
React 16 后废弃部分不安全生命周期,如 componentWillMount,推荐使用 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 替代。
React 18 推出了自动批处理与并发特性,对生命周期感知更强,建议使用函数组件 + Hook 替代类组件管理生命周期。
- React 中 key 的作用是什么?为什么不能用 index?
答:
key 是 React 用于识别列表中元素的唯一标识,在执行 Diff 算法时能更高效定位变动元素。
若使用 index,当列表顺序改变时可能导致组件错误复用、状态错乱、动画异常等问题。
应优先使用唯一且稳定的业务 id 作为 key,只有在列表静态、不会增删改时才可使用 index。
好的,继续为你整理 第 6~10 题 的 React 八股文风格问答:
- 如何在函数组件中模拟 componentDidMount?
答:
函数组件中可使用 useEffect 模拟类组件的 componentDidMount。
写法如下:
useEffect(() => {
// 初始化逻辑
}, []);
当 useEffect 的依赖数组传入空数组 [] 时,表示该副作用函数仅在组件首次渲染时执行一次,行为等价于类组件中的 componentDidMount。
该模式常用于请求初始化数据、设置事件监听、初始化定时器等场景。
- 受控组件和非受控组件的区别?分别适用于什么场景?
答:
React 中,表单组件可分为受控组件和非受控组件。
受控组件:其值受 React state 控制,通过 value 和 onChange 实现双向绑定。
优点是数据可控,易于校验和联动;
非受控组件:直接操作 DOM 读取表单值,通常配合 ref 使用。
适用场景:
受控组件适用于复杂交互和受状态驱动的场景;
非受控组件适用于性能敏感或简单场景,如登录表单、文件上传等。
- React 中事件绑定有哪些方式?有什么坑需要注意?
答:
React 中常见的事件绑定方式包括:
在 JSX 中绑定箭头函数(推荐):
onClick={() => handleClick(id)}
在组件外定义绑定函数:
onClick={handleClick},注意需在 constructor 中 bind:
this.handleClick = this.handleClick.bind(this);
使用 class fields 语法(推荐):
handleClick = () => { … }
注意事项:
每次 render 重新创建箭头函数会影响性能;
函数组件中事件默认具有自动绑定能力,不用显式 bind;
React 事件是合成事件,非原生事件,具备跨浏览器兼容性,支持事件池优化(v17 之后逐步废弃事件池机制)。
好的,以下是 第 11~15 题(Hooks 专题) 的八股文风格标准问答:
- useEffect 有哪些使用场景?依赖数组写错会带来什么问题?
答:
useEffect 是 React 中用于处理副作用的 Hook,常见使用场景包括:
组件挂载时请求数据;
监听事件或订阅操作;
依赖变化时执行操作;
清理副作用(如定时器、订阅)。
其语法如下:
useEffect(() => {
// 副作用逻辑
return () => {
// 清理逻辑
};
}, [dependencies]);
若依赖数组写错(漏依赖、写错变量等),可能导致:
副作用函数未正确触发或频繁执行;
状态不一致、数据不同步;
引发死循环或内存泄漏。
应借助 ESLint 插件(如 eslint-plugin-react-hooks)自动校验依赖项完整性。
- useRef 和 useState 的区别?分别适用于哪些场景?
答:
useRef 和 useState 都可用于在函数组件中存储数据,但用途与行为不同:
useState:用于声明响应式状态,状态变更会触发组件重新渲染;
useRef:用于存储可变值,变更不会触发组件重新渲染,常用于保存 DOM 引用或跨渲染周期保存变量。
适用场景对比如下:
表单输入值:useState;
DOM 节点操作:useRef;
记录上一次状态值或定时器 ID:useRef;
控制重新渲染逻辑:useRef 可避免因频繁更新而导致的性能浪费。
- React 中如何避免 useEffect 死循环?
答:
useEffect 死循环通常由以下原因导致:
依赖数组中包含每次 render 都变化的值(如函数、对象、数组);
副作用函数中更新了依赖项所关联的状态。
解决方式包括:
使用 useCallback 或 useMemo 对函数和对象进行稳定处理;
合理拆分多个 useEffect,细化每个副作用的关注点;
使用空依赖数组 [] 表示只执行一次(等价于 componentDidMount);
严格遵循 ESLint 对依赖项的提示,必要时用注释忽略。
- 如何实现一个自定义 Hook?能举个例子吗?
答:
自定义 Hook 是以 use 开头的函数,用于复用逻辑片段,让组件更简洁、更易维护。
示例:封装一个监听窗口大小的 Hook
import { useState, useEffect } from ‘react’;
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const handleResize = () => {
setSize({ width: window.innerWidth, height: window.innerHeight });
};
window.addEventListener(‘resize’, handleResize);
return () => window.removeEventListener(‘resize’, handleResize);
}, []);
return size;
}
使用方式:
const { width, height } = useWindowSize();
该 Hook 封装了监听逻辑,提升代码复用性和可读性。
- 为什么不能在循环、条件或嵌套函数中调用 Hook?
答:
React 的 Hook 调用必须遵循 “Hook 规则”,即:
只能在函数组件或自定义 Hook 的顶层调用 Hook;
不能在循环、条件、嵌套函数中调用 Hook。
原因是 React 依赖调用顺序识别每个 Hook 的位置。
若在条件或循环中调用 Hook,会导致调用顺序不一致,破坏内部 Hook 栈,从而引发运行时错误或逻辑异常。
官方提供 ESLint 插件 eslint-plugin-react-hooks,可帮助开发者自动检测 Hook 使用规范。
好的,下面是 第 16~20 题(性能优化与实战技巧) 的 React 八股文风格标准问答:
- React 如何优化组件性能?有哪些常见手段?
答:
React 性能优化的目标是减少无效渲染、避免重复计算与状态不一致。常见优化手段包括:
-
使用 React.memo:包装函数组件,避免 props 无变化时重复渲染;
-
使用 useMemo:缓存复杂计算结果,仅在依赖变化时重新计算;
-
使用 useCallback:缓存函数引用,防止子组件因函数变化而重渲染;
-
避免匿名函数和 inline 对象:避免每次 render 创建新引用;
-
拆分组件粒度:将大组件拆分为小组件,提升局部更新效率;
-
懒加载(React.lazy + Suspense):按需加载组件,减少初始包体积;
-
虚拟滚动:使用 react-window、react-virtualized 等库提升长列表渲染性能;
-
开启生产构建优化:确保使用 production 模式打包,开启 tree-shaking 和代码压缩。
- useMemo 和 useCallback 有什么区别?使用场景分别是什么?
答:
useMemo 与 useCallback 都用于性能优化,避免因重新渲染导致的重复计算或函数引用变更。
useMemo(fn, deps):返回 计算结果,常用于缓存变量(如复杂计算、筛选、排序等);
useCallback(fn, deps):返回 函数引用,常用于将函数传入子组件或用于依赖中防止无限循环。
使用场景:
useMemo:优化计算型表达式;
useCallback:优化函数传参与事件处理器。
需注意过度使用反而适得其反,建议仅在明显性能瓶颈场景下使用。
- 什么是 Context?使用时有哪些注意事项?
答:
React Context 是用于跨组件层级共享数据的机制,适用于全局状态传递如主题、用户信息等。核心 API 包括:
const ThemeContext = React.createContext(defaultValue);
<ThemeContext.Provider value={…}>
</ThemeContext.Provider>
在子组件中使用 useContext(ThemeContext) 获取上下文值。
注意事项:
每次 Provider 的 value 变化都会引发所有消费组件重新渲染;
可配合 useMemo 或分离 Provider 组件优化性能;
不适用于频繁变化的局部状态(如输入框内容、临时弹窗);
- React 中如何处理错误?如何实现错误边界?
答:
React 提供错误边界机制处理组件树中的渲染错误(不包括事件、异步逻辑等)。
实现方法:
- 创建类组件,定义 componentDidCatch 和 getDerivedStateFromError:
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
// 上报错误日志
}
render() {
return this.state.hasError ? : this.props.children;
}
}
- 使用方式:将其包裹目标组件
注意:错误边界只能捕获其子组件中的错误。
- React18 中的新特性有哪些?和之前版本有何不同?
答:
React 18 引入了多项新特性,提升并发渲染与用户体验能力:
-
自动批处理(Automatic Batching):多个 state 更新自动合并,减少 render 次数;
-
并发渲染架构(Concurrent Features):通过 startTransition 实现高优先级交互与低优先级更新并行处理;
-
Transition API:可区分紧急与非紧急更新,提升响应性能;
const [isPending, startTransition] = useTransition();
startTransition(() => {
setSearchQuery(input);
});
-
useId:解决 SSR 与客户端 ID 不一致的问题;
-
ReactDOM.createRoot:替代 ReactDOM.render,支持并发模式;
-
Streaming SSR with Suspense:服务器端渲染支持 Suspense 流式输出。
React 18 标志着进入“并发时代”,推荐逐步迁移核心项目使用新版能力。