100+ React 面试题 —— 来自前面试官的直接整理(2026)

100+ React 面试题 ------ 来自前面试官的直接整理(2026)

原文链接

准备 React 面试可能令人畏惧,但获取正确的问题可以带来天壤之别。我们整理了一份包含 100+ 道问题的优先列表及解答,涵盖 React 基础、React Hooks、React Router、React 国际化、React 应用测试以及最新的 React 19 特性等核心主题。

这份全面指南旨在帮助你高效备考、增强信心,确保你在面试中留下深刻印象。

2026 年 5 月更新内容

在下方"React 19 与现代 React"章节新增 10 道关于 React 19 的题目:Actions、useActionStateuseOptimisticuse hook、Server Components、React Compiler 以及 form actions。 已更新现有答案,函数组件和 hooks 设为默认方案,class 组件模式标记为遗留方案。 所有代码示例已针对 React 19 进行验证。

如果你正在寻找更深入的 React 面试准备资料,也可以参考以下资源:

React 基础

掌握 React 基础知识在前端面试中至关重要,因为大多数公司依赖 React 构建现代 Web 应用,面试题通常考察你对组件、状态管理和数据流进行推理的能力。扎实掌握这些核心概念是取得面试成功的关键一步。

1. 什么是 React?它的主要特性有哪些?

React 是由 Facebook 开发的用于创建用户界面的 JavaScript 库,特别适用于单页应用。它支持使用可复用组件,这些组件管理自身的状态。核心优势包括组件驱动架构、通过 Virtual DOM 实现优化更新、声明式编程方式提升可读性,以及强大的社区支持。

查看详细解释并跟踪学习进度 ->

2. 什么是 JSX?它是如何工作的?

JSX 是 JavaScript XML 的缩写,是 JavaScript 的语法扩展,允许在 JavaScript 中编写类似 HTML 的代码。它使构建 React 组件更加便捷。JSX 通常由 Babel 转换为 JavaScript 函数调用。例如,<div>Hello, world!</div> 会被转换为 React.createElement('div', null, 'Hello, world!')

查看详细解释并跟踪学习进度 ->

3. 解释 React 中 Virtual DOM 的概念

Virtual DOM 是 React 使用的实际 DOM 的简化版本。它通过将 Virtual DOM 与真实 DOM 进行比较,仅进行必要的更改(这一过程称为 reconciliation),从而实现高效的 UI 更新。

查看详细解释并跟踪学习进度 ->

4. React 中的 Virtual DOM 如何工作?它的优点和缺点是什么?

React 中的 Virtual DOM 是真实 DOM 的内存表示。当 state 或 props 发生变化时,React 创建一棵新的 Virtual DOM 树,使用 diffing 算法将其与之前的树进行比较,并仅高效更新真实 DOM 中发生变化的部分。

  • 优点:减少昂贵的直接 DOM 操作,提升性能;使 UI 更新声明式和可预测。
  • 缺点:diffing 和额外内存使用会有一定开销;在高度动态的 UI 中,不一定总比手动优化更好。

查看详细解释并跟踪学习进度 ->

5. React Node、React Element 和 React Component 之间有什么区别?

  • React Node :指任何可以在 React 中渲染的单元,如 element、字符串、数字或 null
  • React Element :是一个不可变对象,定义了应该渲染什么,通常使用 JSX 或 React.createElement 创建。
  • React Component:是一个返回 React Elements 的函数或 class,用于创建可复用的 UI 组件。

查看详细解释并跟踪学习进度 ->

6. React Fragments 的用途是什么?

React Fragments 允许你将多个元素组合在一起,而无需向 DOM 添加额外的节点。当你需要从组件返回多个元素但不想将它们包装在容器元素中时,它们特别有用。你可以使用简写语法 <>...</>React.Fragment

jsx 复制代码
return (  
   <>
    <ChildComponent1 />
    <ChildComponent2 />
   </>
);

查看详细解释并跟踪学习进度 ->

7. React 中 key prop 的作用是什么?

在 React 中,key prop 用于唯一标识列表中的元素,使 React 能够更高效地更新和重新排序元素来优化渲染。没有唯一的 key,React 可能会不必要地重新渲染元素,导致性能问题和潜在的 bug。

jsx 复制代码
{  
    items.map((item) => <ListItem key={item.id} value={item.value} />);
}

查看详细解释并跟踪学习进度 ->

8. 在 React 中使用数组索引作为 key 会有什么后果?

使用数组索引作为 key 可能导致性能问题和意外行为,尤其是在重新排序或删除元素时。React 依赖 key 来唯一标识元素,使用索引可能导致组件不必要地重新渲染或显示不正确的数据。

查看详细解释并跟踪学习进度 ->

9. React 中的 props 是什么?它们与 state 有何不同?

Props(properties 的缩写)是 React 组件的输入,允许你从父组件向子组件传递数据。它们是不可变的,用于配置组件。相比之下,state 是组件内部的数据,可以随时间变化,通常由用户交互或其他事件触发。

查看详细解释并跟踪学习进度 ->

10. React 的 class 组件和函数组件之间有什么区别?

Class 组件是扩展 React.Component 的 ES6 class,依赖生命周期方法(componentDidMountcomponentDidUpdate 等)和 this.state。函数组件是纯函数,接收 props 作为输入并返回 JSX,使用 hooks(useStateuseEffectuseRef 等)管理状态和副作用。自 React 16.8 引入 hooks 以来,函数组件已成为新代码的默认选择;class 组件仅为向后兼容保留,不再是推荐模式。

11. 什么时候应该使用 class 组件而不是函数组件?

默认使用函数组件。Class 组件已属遗留方案:新的 API(如 Suspense 数据获取、use hook、Actions、Server Components 和 React Compiler)仅支持函数组件。今天编写 class 组件唯一剩下的理由是实现 error boundary,它仍然需要 static getDerivedStateFromError / componentDidCatch

12. 什么是 React Fiber?

React Fiber 是对 React 核心算法的完全重写,旨在提升性能并支持异步渲染、error boundaries 和增量渲染等新特性。它将渲染过程分解为更小的单元,使 React 能够根据需要暂停、中止或优先处理更新。

查看详细解释并跟踪学习进度 ->

13. 什么是 Reconciliation?

Reconciliation 是 React 高效更新 DOM 以匹配 Virtual DOM 的过程。它涉及比较新的 Virtual DOM 树与之前的树,并确定更新实际 DOM 所需的最少更改。这一过程通过避免不必要的重新渲染来确保最佳性能。

查看详细解释并跟踪学习进度 ->

14. Shadow DOM 和 Virtual DOM 有什么区别?

Shadow DOM 是一种 Web 标准,用于封装 DOM 的一部分,将其与文档其余部分隔离。它用于创建可复用的自包含组件,不影响全局样式或脚本。

Virtual DOM 是实际 DOM 的内存表示,用于优化渲染。它比较 UI 的当前状态和先前状态,仅更新 DOM 中必要的部分,从而提升性能。

15. 受控组件和非受控组件有什么区别?

在受控组件中,表单数据通过组件 state 管理,state 是唯一的真实数据源。输入值的变化由事件处理程序处理。在非受控组件中,表单状态在内部管理并通过 refs 访问。受控组件提供更多控制且更易于测试,而非受控组件在基本场景中更简单。

受控组件示例:

jsx 复制代码
function ControlledInput() {  const [value, setValue] = React.useState('');  return (    <input      type="text"      value={value}      onChange={(e) => setValue(e.target.value)}    />  );}

非受控组件示例:

jsx 复制代码
function UncontrolledInput() {  const inputRef = React.useRef();  return <input type="text" ref={inputRef} />;}

查看详细解释并跟踪学习进度 ->

16. 如何在 React 应用中提升 state?为什么有必要这样做?

在 React 中提升 state 是指将 state 从子组件移动到它们最近的共同祖先组件。这种模式用于在非直接父子关系的组件之间共享 state。通过提升 state,可以避免 prop drilling 并简化共享数据的管理。

jsx 复制代码
const Parent = () => {  const [counter, setCounter] = useState(0);
  return (    <div>      <Child1 counter={counter} />      <Child2 setCounter={setCounter} />    </div>  );};
const Child1 = ({ counter }) => <h1>{counter}</h1>;const Child2 = ({ setCounter }) => (  <button onClick={() => setCounter((prev) => prev + 1)}>Increment</button>);

在此示例中,state 在 Parent 组件中管理,两个子组件通过 props 访问它。

17. 什么是 Pure Components?

React 中的 Pure Components 是仅在其 props 或 state 发生变化时才重新渲染的组件。它们使用浅比较来检查 props 或 state 是否发生变化,防止不必要的重新渲染并提升性能。

  • Class 组件可以通过继承 React.PureComponent 成为 Pure Component
  • 函数组件可以使用 React.memo 达到同样效果
jsx 复制代码
const PureFunctionalExample = React.memo(function ({ value }) {  return <div>{value}</div>;});

使用 React Compiler 后,几乎不需要手动使用 React.memouseMemouseCallback;编译器会自动插入等效的 memoization。

18. createElementcloneElement 有什么区别?

React 中 createElement 和 cloneElement 的区别如下:

createElement:
  • 用于创建新的 React element。
  • 接收元素类型(如 'div'、React 组件)、props 和 children,返回新的 React element。
  • 通常由 JSX 内部使用或用于动态创建元素。示例:
javascript 复制代码
React.createElement('div', { className: 'container' }, 'Hello World');
cloneElement:
  • 用于克隆现有的 React element 并可选择性地修改其 props。
  • 允许你克隆 React element 并传递新的 props 或覆盖现有 props,保留原始元素的 children 和 state。
  • 适用于需要在不重新创建元素的情况下操作元素。示例:
jsx 复制代码
const element = <button className="btn">Click Me</button>;const clonedElement = React.cloneElement(element, { className: 'btn-primary' });

19. React 中 PropTypes 的作用是什么?

PropTypes 是 React 的运行时 prop 类型检查器。你可以声明期望的类型,在开发模式下类型不匹配时 React 会在控制台发出警告。

jsx 复制代码
import PropTypes from 'prop-types';
function MyComponent({ name, age }) {  return (    <div>      {name} is {age} years old    </div>  );}
MyComponent.propTypes = {  name: PropTypes.string.isRequired,  age: PropTypes.number.isRequired,};

PropTypes 自 React 19 起已弃用,不再随 react 包一起发布。请改用 TypeScript ------ 它在编译时就能捕获同样的类型不匹配,并与编辑器工具集成。

20. 什么是无状态组件?

React 中的无状态组件是不管理或不持有任何内部 state 的组件。它们仅通过 props 接收数据并根据这些数据渲染 UI。这些组件通常是函数组件,用于展示目的。

关键点:
  • 不使用 this.state
  • 基于 props 渲染 UI
  • 专注于展示信息,而非管理行为
jsx 复制代码
function StatelessComponent({ message }) {  return <div>{message}</div>;}

无状态组件更简单、更易于测试,通常也更可复用。随着 hooks 的引入,React 组件大多使用函数编写,并可通过 useState hook 包含 state。

21. 什么是有状态组件?

React 中的有状态组件是管理并持有自身内部 state 的组件。它们可以根据用户交互或其他事件修改 state,并在 state 变化时重新渲染自身。

关键点:
  • 使用 this.state(class 组件)或 useState(函数组件)
  • 可以通过事件处理程序或生命周期方法更新 state
  • 处理逻辑和数据管理
jsx 复制代码
function StatefulComponent() {  const [count, setCount] = React.useState(0);
  return (    <div>      <p>{count}</p>      <button onClick={() => setCount(count + 1)}>Increment</button>    </div>  );}

有状态组件对于处理动态和交互式 UI 至关重要。

22. React 组件 props 类型检查的推荐方式是什么?

使用 TypeScript。它在编译时捕获 prop 类型不匹配,与编辑器工具(自动完成、重构、跳转到定义)集成,并且是大多数 React 项目模板的默认选择。

tsx 复制代码
type MyComponentProps = {  name: string;  age: number;};
function MyComponent({ name, age }: MyComponentProps) {  return (    <div>      {name} is {age} years old    </div>  );}

旧方案是 PropTypes,一个在开发模式下 prop 类型不匹配时发出警告的运行时检查器。它在 React 19 中已弃用,不再随 react 包一起发布。如果你维护的代码库仍在使用 prop-types,请迁移到 TypeScript。

23. React 为什么不推荐直接修改 state?

React 不推荐直接修改 state,因为这可能导致意外行为和 bug。state 的不可变性有助于高效判断组件何时需要重新渲染;直接修改可能会阻止 React 检测到变化。

查看详细解释并跟踪学习进度 ->

React Hooks

掌握 React hooks 在前端面试中非常重要,因为 hooks 是现代 React 中管理 state、副作用和组件生命周期的标准方式。展示对 hooks 的扎实理解,表明你可以编写清晰的函数组件并解决复杂问题,而无需依赖过时的 class 模式。

24. 在 React 中使用 hooks 有哪些好处?

Hooks 允许在函数组件中使用 state 和其他 React 特性,取代了对 class 组件的需求。它们通过减少对生命周期方法的依赖来简化代码,增强可读性,并促进有状态逻辑在组件之间的复用。

常用的 hooks 如 useStateuseEffect 用于管理 state 和副作用。

查看详细解释并跟踪学习进度 ->

25. React hooks 的规则是什么?

React hooks 应该在函数的顶层调用,不能在循环、条件语句或嵌套函数中调用。它们只能在 React 函数组件或自定义 hooks 中使用。这些准则确保了正确的 state 管理和生命周期行为。

查看详细解释并跟踪学习进度 ->

26. React 中 useEffectuseLayoutEffect 有什么区别?

useEffectuseLayoutEffect 都用于在 React 函数组件中处理副作用,但执行时机不同:

  • useEffect:在 DOM 渲染完成后异步运行,适用于数据获取或订阅等任务。
  • useLayoutEffect:在 DOM 更新后但浏览器绘制前同步运行,适用于测量 DOM 元素或使 UI 与 DOM 对齐等任务。示例:
jsx 复制代码
import React, { useEffect, useLayoutEffect, useRef } from 'react';
function Example() {  const ref = useRef();
  useEffect(() => {    console.log('useEffect: Runs after DOM paint');  });
  useLayoutEffect(() => {    console.log('useLayoutEffect: Runs before DOM paint');    console.log('Element width:', ref.current.offsetWidth);  });
  return <div ref={ref}>Hello</div>;}

查看详细解释并跟踪学习进度 ->

27. useEffect 的依赖数组有什么影响?

useEffect 的依赖数组控制 effect 何时重新运行:

  • 如果为空,effect 仅在初始渲染后运行一次。
  • 如果包含变量,effect 在任意依赖变量变化时重新运行。
  • 如果省略,effect 在每次渲染后都运行。

查看详细解释并跟踪学习进度 ->

28. React 中的 useRef hook 是什么?应该在什么情况下使用?

useRef hook 创建一个在渲染之间持久化的可变对象,允许直接访问 DOM 元素、存储可变值而不触发重新渲染,并维护对值的引用。

例如,useRef 可用于聚焦输入元素:

jsx 复制代码
import React, { useRef, useEffect } from 'react';
function TextInputWithFocusButton() {  const inputEl = useRef(null);  useEffect(() => {    inputEl.current.focus();  }, []);  return <input ref={inputEl} type="text" />;}

查看详细解释并跟踪学习进度 ->

29. React class 组件中 setState() 的回调函数参数形式的作用是什么?应该在什么时候使用?

这适用于 class 组件,class 组件已不再是推荐模式。函数组件的等效方案(useState 的更新函数形式)在末尾展示。

setState() 的回调(更新函数)形式确保 state 更新基于最新的 state 和 props。当新 state 依赖前一个 state 时这一点很重要,因为 React 可能会批量处理多个更新,直接读取的 this.state 可能已经过时。

javascript 复制代码
this.setState((prevState, props) => ({  counter: prevState.counter + props.increment,}));

函数组件的等效方案使用 useState 的更新函数形式:

javascript 复制代码
const [counter, setCounter] = useState(0);setCounter((prev) => prev + props.increment);

查看详细解释并跟踪学习进度 ->

30. React 中的 useCallback hook 是什么?应该在什么时候使用?

useCallback hook 对函数进行 memoization,防止每次渲染时重新创建。这在将回调函数传递给依赖引用相等性来避免不必要渲染的优化子组件时特别有用。当函数作为 prop 传递给 memoized 子组件时使用。

javascript 复制代码
const memoizedCallback = useCallback(() => {  doSomething(a, b);}, [a, b]);

启用 React Compiler 后,你很少需要手动使用 useCallback;编译器会自动插入等效的 memoization。

查看详细解释并跟踪学习进度 ->

31. React 中的 useMemo hook 是什么?应该在什么时候使用?

useMemo hook 对高开销的计算进行 memoization,仅在依赖项变化时重新计算。这通过避免不必要的重新计算来提升性能。应用于不需要在每次渲染时都运行的计算密集型函数。

javascript 复制代码
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

启用 React Compiler 后,你很少需要手动使用 useMemo;编译器会自动 memoize 派生值。

查看详细解释并跟踪学习进度 ->

32. React 中的 useReducer hook 是什么?应该在什么时候使用?

useReducer hook 在函数组件中管理复杂的 state 逻辑,是 useState 的替代方案。当 state 有多个字段(且对如何修改它们有约束),或下一个 state 依赖前一个 state 时,它是理想选择。

useReducer hook 接收一个 reducer 函数和一个初始 state。reducer 函数接收当前的 stateaction,返回新的 state。

javascript 复制代码
const [state, dispatch] = useReducer(reducer, initialState);

查看详细解释并跟踪学习进度 ->

33. React 中的 useId hook 是什么?应该在什么时候使用?

useId hook 为组件内的元素生成唯一 ID,这对于无障碍访问至关重要,它可以动态创建 ID 用于关联表单输入和标签。即使组件多次渲染,它也能保证在整个应用中生成唯一 ID。

jsx 复制代码
import { useId } from 'react';
function MyComponent() {  const id = useId();
  return (    <div>      <label htmlFor={id}>Name:</label>      <input id={id} type="text" />    </div>  );}

查看详细解释并跟踪学习进度 ->

34. 你能解释如何在 React 中创建和使用自定义 hooks 吗?

在 React 中创建和使用自定义 hooks:

  1. 创建一个以 use 开头的函数,并在其中使用内置 hooks(如 useStateuseEffect
  2. 返回你想要共享的值或函数。

示例:

javascript 复制代码
function useForm(initialState) {  const [formData, setFormData] = useState(initialState);  const handleChange = (e) =>    setFormData({ ...formData, [e.target.name]: e.target.value });  return [formData, handleChange];}

使用 Hook:

jsx 复制代码
function MyForm() {  const [formData, handleChange] = useForm({ name: '', email: '' });  return <input name="name" value={formData.name} onChange={handleChange} />;}

自定义 hooks 让你在组件之间复用逻辑,保持代码整洁。

高级概念

掌握 React 的高级概念(如 Suspense、forwardRef() 和 context)表明你能够处理性能优化、代码分割和复杂组件模式。在面试中,这展示了你已准备好构建可扩展、可维护的应用,而不仅仅是基本的组件逻辑。

35. React 中的重新渲染是什么意思?

在 React 中,重新渲染是指根据组件 state 或 props 的变化更新用户界面(UI)的过程。当组件的 state 或 props 发生变化时,React 重新渲染组件以在 UI 中反映更新后的数据。

这包括:

  1. 重新计算组件返回的 JSX
  2. 将新的 JSX 与之前的进行比较(使用 Virtual DOM)
  3. 仅更新真实 DOM 中的差异部分(高效渲染)
  4. 重新渲染确保 UI 与组件的 state 和 props 保持同步

查看详细解释并跟踪学习进度 ->

36. React 中的 forwardRef() 用于什么?

在 React 19 之前,函数组件不接受 ref 作为常规 prop,因此使用 forwardRef()ref 传递给子 DOM 元素。

jsx 复制代码
import React, { forwardRef } from 'react';
const MyComponent = forwardRef((props, ref) => <input ref={ref} {...props} />);

在 React 19 中,ref 是函数组件的常规 prop,forwardRef 已弃用。直接从 props 中解构即可:

jsx 复制代码
function MyComponent({ ref, ...props }) {  return <input ref={ref} {...props} />;}

查看详细解释并跟踪学习进度 ->

37. React 中的 Error Boundaries 用于什么?

Error boundaries 捕获其子组件中的 JavaScript 错误,记录错误日志,并显示回退 UI,而不是让整个应用崩溃。它们使用 componentDidCatchstatic getDerivedStateFromError 方法,但不会捕获事件处理程序或异步代码中的错误。

查看详细解释并跟踪学习进度 ->

38. 什么是 React Suspense?

React Suspense 允许在组件中更优雅地处理异步操作。它在等待资源(如数据或代码)加载时提供回退内容。你可以将其与 React.lazy() 一起使用以实现代码分割。

jsx 复制代码
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function MyComponent() {  return (    <React.Suspense fallback={<div>Loading...</div>}>      <LazyComponent />    </React.Suspense>  );}

查看详细解释并跟踪学习进度 ->

39. 解释什么是 React Hydration?

Hydration 涉及在客户端附加事件监听器并使服务端渲染的 HTML 具有交互性。在服务端渲染后,React 通过附加事件处理程序来初始化动态行为。

查看详细解释并跟踪学习进度 ->

40. React Portals 用于什么?

React Portals 允许将子元素渲染到父组件 DOM 层级之外的 DOM 节点中。这对于需要突破父级 overflow 或 z-index 约束的模态框或 tooltip 很有用。

查看详细解释并跟踪学习进度 ->

41. 什么是 React Strict Mode?它有哪些好处?

React Strict Mode 是 React 中的一个开发特性,用于激活额外的检查和警告,帮助识别应用中的潜在问题。

  • 检测不安全的生命周期:警告已弃用的生命周期方法
  • 识别副作用:高亮渲染方法中有副作用的组件
  • 警告意外的 state 变化:捕获意外的 state 修改
  • 强制执行最佳实践:标记潜在问题,鼓励使用现代模式
jsx 复制代码
<React.StrictMode>  <App /></React.StrictMode>

<React.StrictMode> 包裹组件会激活这些开发检查,不影响生产构建。

42. React 应用中的代码分割是什么?

代码分割通过将代码划分为更小的块并按需加载来提升性能,从而减少初始加载时间。可以通过动态 import() 语句或使用 React 的 React.lazy 和 Suspense 来实现。

jsx 复制代码
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {  return (    <React.Suspense fallback={<div>Loading...</div>}>      <LazyComponent />    </React.Suspense>  );}

查看详细解释并跟踪学习进度 ->

43. 如何优化 React context 的性能以减少重新渲染?

优化 context 性能包括使用 useMemo 对 context 值进行 memoization、拆分 context 以隔离 state 变化,以及使用选择器(selector)仅重新渲染必要的组件。

javascript 复制代码
const value = useMemo(() => ({ state, dispatch }), [state, dispatch]);

查看详细解释并跟踪学习进度 ->

44. 什么是 Flux 模式?

Flux 模式通过单向数据流管理应用 state,通过 Dispatcher、Stores、Actions 和 Views 之间清晰的关注点分离,简化调试并增强可维护性。

查看详细解释并跟踪学习进度 ->

45. 解释 React 的单向数据流

在 React 中,单向数据流意味着数据通过 props 从父组件流向子组件。

  • 父到子:父组件将数据传递给子组件
  • State 更新:要更改数据,子组件调用父组件传递下来的函数

示例:

jsx 复制代码
function Parent() {  const [count, setCount] = React.useState(0);  return <Child count={count} increment={() => setCount(count + 1)} />;}
function Child({ count, increment }) {  return <button onClick={increment}>Count: {count}</button>;}

这确保数据沿一个方向流动,使应用更可预测。

查看详细解释并跟踪学习进度 ->

46. 在 React 中使用 context 有哪些陷阱?

React 中的 context 如果处理不当可能导致性能问题,即使只有部分 context 发生变化,也会导致消费该 context 的组件不必要地重新渲染。过度使用 context 进行 state 管理也会使代码更难维护和理解。最好谨慎使用 context,对于更复杂的场景考虑使用 Redux 或 Zustand 等其他 state 管理方案。

查看详细解释并跟踪学习进度 ->

47. React 中有哪些反模式?

React 反模式是可能导致代码效率低下或难以维护的做法。常见示例包括:

  • 直接修改 state 而不是使用 state setter
  • 使用 useEffect 从 props 派生 state(应在渲染期间计算)
  • 将可以从其他 state 或 props 计算出的数据放入 state
  • 列表不使用 key,或对可重排序的列表使用数组索引作为 key
  • Effect 缺少或有过时的依赖项
  • 深层嵌套的 state;优先使用扁平结构配合 useReducer 或 state 库
  • 在渲染期间读取或写入 refs(应在 effect 或事件处理程序中进行)
  • 对不驱动渲染的值使用 useState(改用 useRef
  • 在条件语句或循环中调用 hooks(违反 Hooks 规则)

旧的 class 组件反模式(使用 componentWillMount 进行数据获取或依赖 componentWillReceiveProps)涉及的 API 已被重命名为 UNSAFE_*,不再适用于函数组件代码。

查看详细解释并跟踪学习进度 ->

48. 如何在 React state、context 和外部 state 管理器之间做出选择?

在 React state、context 和外部 state 管理器之间选择取决于你的应用复杂度。对于局部组件 state 使用 React state,对于跨多个组件共享的全局 state 使用 context,对于需要优化重新渲染等高级特性的复杂 state 管理使用 Redux 或 MobX 等外部管理器。

查看详细解释并跟踪学习进度 ->

49. 解释在 React 中调用 setState 时会发生什么?

在 React 中调用 setState 时:

  1. State 更新:更新组件的 state,触发组件重新渲染
  2. 批处理:React 可能会将多个 setState 调用批量合并为一次更新以优化性能
  3. 重新渲染:React 使用新 state 重新渲染组件(以及需要的子组件)
  4. 异步:State 更新可能是异步的,意味着 React 不会立即应用 state 更改;它会调度到稍后执行以优化性能

示例:

jsx 复制代码
function Counter() {  const [count, setCount] = React.useState(0);
  const increment = () => {    setCount(count + 1);   };
  return <button onClick={increment}>Count: {count}</button>;}

在此示例中,调用 setState(通过 setCount)会触发带有更新后 count 的重新渲染。

50. 解释 prop drilling

Prop drilling 是指通过 props 将数据从父组件传递到深层嵌套的子组件,即使中间组件并不使用这些数据。

示例:

jsx 复制代码
function Grandparent() {  const data = 'Hello from Grandparent';  return <Parent data={data} />;}
function Parent({ data }) {  return <Child data={data} />;}
function Child({ data }) {  return <p>{data}</p>;}

在此示例中,data 经过了多个组件的传递,尽管只有 Child 组件使用它。prop drilling 在组件层级较浅的小型应用中是可以接受的。当需要在应用的更深层级访问全局 state 时,使用 context 和/或外部 state 管理器可能更好。

51. 描述 React 中的懒加载

React 中的懒加载是一种技术,组件仅在需要时才加载,而不是在初始页面加载时加载。这有助于通过将代码拆分为更小的块来减少初始加载时间并提升性能。

示例:

jsx 复制代码
import React, { Suspense, lazy } from 'react';
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {  return (    <Suspense fallback={<div>Loading...</div>}>      <LazyComponent />    </Suspense>  );}

在此示例中,LazyComponent 仅在其被渲染时才加载,加载期间显示回退 UI(Loading...)。

52. 讨论 React 中的合成事件

React 中的合成事件是对原生 DOM 事件的封装,确保跨浏览器行为一致。它们规范化了事件处理方式,为 React 应用提供统一的 API。

这些事件封装在 SyntheticEvent 对象中,暴露了常用的方法如 preventDefault()stopPropagation()。自 React 17 起,根事件监听器附加到 React root 容器(而非 document),这使得嵌套的 React 树能够正确协作。

示例:

jsx 复制代码
function MyComponent() {  const handleClick = (event) => {    event.preventDefault();    console.log('Button clicked');  };
  return <button onClick={handleClick}>Click me</button>;}

旧资料中提到的 event pooling(React 在事件处理程序运行后重用事件对象,使事件在异步代码中不可用)已在 React 17 中移除,因此你现在可以异步读取或传递事件对象,无需调用 event.persist()

53. 解释 React class 组件的生命周期方法

Class 生命周期方法仅适用于 class 组件,class 组件已不再是推荐模式。函数组件的等效方案(使用 useEffect)在末尾展示。

React class 组件在不同阶段有不同的生命周期方法:

挂载阶段:
  • constructor:初始化 state 或绑定方法
  • componentDidMount:组件挂载后运行,适用于 API 调用或订阅
javascript 复制代码
componentDidMount() {  console.log('Component mounted');}
更新阶段:
  • shouldComponentUpdate:决定组件是否应重新渲染
  • componentDidUpdate:更新后运行,适用于处理副作用
卸载阶段:
  • componentWillUnmount:清理(例如移除事件监听器)。
javascript 复制代码
componentWillUnmount() {  console.log('Component will unmount');}

在函数组件中,以上所有都可以用 useEffect 表达:

javascript 复制代码
useEffect(  () => {        console.log('Mounted or updated');    return () => {            console.log('Will unmount');    };  },  [      ],);

54. React 中的并发特性是什么?它们如何提升渲染性能?

并发特性在 React 18 中引入(React 17 中的实验性"Concurrent Mode"品牌已不再使用)。它们允许 React 暂停、中断和恢复渲染工作,而不是将其作为单个阻塞过程运行。这使 UI 保持响应:紧急更新(如输入或点击)可以抢占较慢的工作(如渲染大型列表或过滤搜索结果)。

这些特性通过特定的 API(useTransitionuseDeferredValue 和用于数据获取的 <Suspense>)选择加入,而非全局模式开关。

55. React 如何处理并发渲染中的多个更新并对其进行优先级排序?

React 的调度器根据更新的触发方式分配优先级。来自直接用户交互(点击、输入、聚焦)的更新被视为紧急更新并同步渲染。包装在 startTransition 中或通过 useDeferredValue 读取的更新是非紧急的:React 可以中断它们以处理更紧急的更新,然后恢复。这就是为什么繁重的过滤或列表渲染可以与搜索框中的输入共存而不阻塞它。

56. 如何在 React 应用中处理长时间运行的任务或高开销计算而不阻塞 UI?

为避免阻塞 UI,使用 Web WorkerssetTimeoutrequestIdleCallback 来卸载繁重的计算。或者,将任务分解为更小的部分,并使用 React 的 Suspense 或 useMemo 仅在必要时重新计算。

使用 setTimeout 延迟计算的示例:

javascript 复制代码
const [data, setData] = useState(null);
useEffect(() => {  setTimeout(() => {    const result = computeExpensiveData();    setData(result);  }, 0);}, []);

57. 解释 React 应用的服务端渲染及其好处

服务端渲染(SSR)涉及在服务器上渲染组件,然后将完全渲染好的 HTML 发送给客户端,通过高效的 hydration 过程改善初始加载时间和 SEO。

查看详细解释并跟踪学习进度 ->

58. 解释 React 应用的静态生成

静态生成在构建时而非运行时预渲染 HTML;这种方法通过快速交付静态内容来提升性能,同时改善 SEO 效果。

查看详细解释并跟踪学习进度 ->

59. React 中的高阶组件是什么?

高阶组件(HOC)是接收一个组件并返回一个带有额外 props 或行为的新组件的函数,促进组件之间的逻辑复用。

jsx 复制代码
const withExtraProps = (WrappedComponent) => {  return (props) => <WrappedComponent {...props} extraProp="value" />;};
const EnhancedComponent = withExtraProps(MyComponent);

HOC 在 hooks 出现之前是跨切面逻辑(认证、数据获取、主题)的主流模式。如今自定义 hooks 几乎覆盖了所有这些用例,且无需包装组件的嵌套。HOC 仍然出现在旧代码库和一些库中(如 react-reduxconnect),但新代码应优先使用自定义 hook。

查看详细解释并跟踪学习进度 ->

60. 解释 React 中的展示组件 vs 容器组件模式

展示组件 vs 容器组件模式将组件分为两个角色:展示组件 处理渲染(标记、样式)并通过 props 接收数据,而容器组件处理 state、数据获取和行为,然后将数据向下传递。

jsx 复制代码
function UserListContainer() {  const [users, setUsers] = useState([]);  useEffect(() => {    fetchUsers().then(setUsers);  }, []);  return <UserList users={users} />;}
function UserList({ users }) {  return (    <ul>      {users.map((u) => (        <li key={u.id}>{u.name}</li>      ))}    </ul>  );}

这种模式在 hooks 出现之前很流行;其原始作者(Dan Abramov)后来表示不再值得作为硬性规则遵循。使用 hooks 后,同样的分离通常通过提取自定义 hook(如 useUsers())来表达,而非包装组件。新代码通常将两个角色融合到一个组件加一个自定义 hook 中。

查看详细解释并跟踪学习进度 ->

61. React 中的 Render Props 是什么?

React 中的 render props 允许通过一个作为函数的 prop 在组件之间共享代码。这个函数返回一个 React element,使得数据可以传递给子组件。

jsx 复制代码
function DataFetcher({ url, render }) {  const [data, setData] = useState(null);  useEffect(() => {    fetch(url)      .then((res) => res.json())      .then(setData);  }, [url]);  return render(data);}
<DataFetcher  url="/api/data"  render={(data) => <div>{data ? data.name : 'Loading...'}</div>}/>;

与 HOC 一样,render props 是 hooks 出现之前共享有状态逻辑的方案。如今这些用例大多通过自定义 hook(const data = useFetch(url))解决,组合更自然且避免了 render-prop 金字塔。Render props 在消费者需要根据父组件管理的 state 控制渲染内容的狭窄场景中仍然有用(如 headless 组件库)。

查看详细解释并跟踪学习进度 ->

62. 解释 React 中的组合模式

React 中的组合模式涉及通过组合更小的可复用组件来构建组件,而不是使用继承。这鼓励通过将组件作为 children 或 props 传递来创建复杂的 UI。

jsx 复制代码
function WelcomeDialog() {  return (    <Dialog>      <h1>Welcome</h1>      <p>Thank you for visiting our spacecraft!</p>    </Dialog>  );}
function Dialog(props) {  return <div className="dialog">{props.children}</div>;}

查看详细解释并跟踪学习进度 ->

63. 如何在浏览器调整大小时重新渲染视图?

要在浏览器调整大小时重新渲染视图,使用 useEffect hook 监听 resize 事件并更新 state。

示例:

jsx 复制代码
import React, { useState, useEffect } from 'react';
function ResizeComponent() {  const [windowWidth, setWindowWidth] = useState(window.innerWidth);
  useEffect(() => {    const handleResize = () => setWindowWidth(window.innerWidth);    window.addEventListener('resize', handleResize);    return () => window.removeEventListener('resize', handleResize);  }, []);
  return <div>Window width: {windowWidth}px</div>;}
export default ResizeComponent;

每当窗口大小调整时,这会更新 state 并重新渲染组件。

64. 如何在 React 应用中处理异步数据加载?

异步数据加载使用 useEffect 配合 useState hooks;在 useEffect 中获取数据,用获取的结果更新 state,确保组件使用新数据重新渲染。

jsx 复制代码
import React, { useState, useEffect } from 'react';
const FetchAndDisplayData = () => {  const [info, updateInfo] = useState(null);  const [isLoading, toggleLoading] = useState(true);
  useEffect(() => {    const retrieveData = async () => {      try {        const res = await fetch('https://api.example.com/data');        const data = await res.json();        updateInfo(data);      } catch (err) {        console.error('Error fetching data:', err);      } finally {        toggleLoading(false);      }    };
    retrieveData();  }, []);
  return (    <div>      {isLoading ? (        <p>Fetching data, please wait...</p>      ) : (        <pre>{JSON.stringify(info, null, 2)}</pre>      )}    </div>  );};
export default FetchAndDisplayData;

查看详细解释并跟踪学习进度 ->

65. 在 React 中进行数据获取时有哪些常见陷阱?

React 中数据获取的常见陷阱包括:未能处理加载和错误状态、忘记清理订阅导致内存泄漏、以及不正确使用生命周期方法或 hooks。始终确保正确处理这些状态、在组件卸载时清理,并在函数组件中使用 useEffect 处理副作用。

查看详细解释并跟踪学习进度 ->

React Router

理解 React Router 在前端面试中很重要,因为大多数实际应用需要客户端路由来处理导航、动态 URL 和嵌套布局。熟练使用路由表明你能够有效地构建应用结构并提供流畅的用户体验。

66. 什么是 React Router?

React Router 是 React 应用的一个流行路由库,支持基于 URL 在不同组件之间导航。它提供声明式路由,允许你以简单直观的方式定义路由及其对应组件。

67. React Router 如何工作?如何实现动态路由?

React Router 将 URL 路径映射到组件,实现单页应用中的导航。动态路由允许你使用 URL 参数根据动态值渲染组件。

jsx 复制代码
import { BrowserRouter, Routes, Route, useParams } from 'react-router-dom';
function UserPage() {  const { id } = useParams();   return <h1>User ID: {id}</h1>;}
export default function App() {  return (    <BrowserRouter>      <Routes>        <Route path="/user/:id" element={<UserPage />} /> {}      </Routes>    </BrowserRouter>  );}

关键特性

  • 动态段::id 从 URL 捕获动态数据。
  • useParams Hook:访问这些动态值用于渲染。

68. 如何在 React Router 中处理嵌套路由和路由参数?

嵌套路由允许你创建组件层级,useParams 帮助访问动态路由参数。

关键技术

  • <Outlet>:在父布局中渲染子路由
  • useParams:检索动态路由参数
jsx 复制代码
import {  BrowserRouter,  Routes,  Route,  Outlet,  useParams,} from 'react-router-dom';
function UserProfile() {  const { userId } = useParams();  return <h2>User ID: {userId}</h2>;}
function App() {  return (    <BrowserRouter>      <Routes>        <Route path="user/:userId" element={<Outlet />}>          <Route path="profile" element={<UserProfile />} />        </Route>      </Routes>    </BrowserRouter>  );}

69. BrowserRouter 和 HashRouter 有什么区别?

  • BrowserRouter :使用 HTML5 History API 管理导航,实现不带 hash(#)的干净 URL。需要服务端配置来正确处理路由,尤其是深度链接。
  • HashRouter :使用 URL 的 hash(#)部分来模拟导航。不需要服务端配置,因为 hash 永远不会发送到服务器。适用于无法进行服务端路由的环境(如静态托管)。

70. React Router 与 history 库有何不同?

React Router 是 React 的路由库,提供声明式 API 来定义路由和处理导航。它管理组件和 URL。

History 库是一个低级工具,仅管理浏览器历史记录(如 push 和 pop 历史条目)。它不处理 UI 渲染或路由,因此更通用且不特定于 React。

React Router 内部使用 history 库,但添加了路由和组件管理等额外功能。

71. React Router v6 有哪些 <Router> 组件?

在 React Router v6 中,关键的 <Router> 组件有:

  • <BrowserRouter>:使用 HTML5 history API 使 UI 与 URL 保持同步。通常用于 Web 应用。
  • <HashRouter>:使用 URL hash 片段(#)管理路由,适用于静态文件托管或不支持 HTML5 history API 的旧浏览器。
  • <MemoryRouter>:将 URL 保存在内存中(地址栏不变),适用于非浏览器环境如测试或嵌入式应用。
  • <StaticRouter>:用于服务端渲染(SSR),路由在无浏览器环境下处理,通常在 Node.js 环境中。

每种 Router 服务于不同的用例,但在 React 应用中提供相同的路由功能。

72. history 的 push 和 replace 方法的作用是什么?

history 库的 push 和 replace 方法用于管理浏览器历史堆栈和控制导航。

push:
  • 向历史堆栈添加新条目,用户可以使用浏览器的后退按钮导航回去。
  • 示例:history.push('/new-page')
replace:
  • 用新条目替换历史堆栈中的当前条目,用户不能使用后退按钮返回上一页。
  • 示例:history.replace('/new-page')

73. 如何在 React Router 中编程式导航?

在 React Router v6 中,你可以通过使用 useNavigate hook 进行编程式导航。首先从 react-router-dom 导入 useNavigate 并调用它获取 navigate 函数。然后可以使用 navigate('/new-page') 导航到不同路由。

例如:

jsx 复制代码
import { useNavigate } from 'react-router-dom';
function MyComponent() {  const navigate = useNavigate();  const goToPage = () => navigate('/new-page');  return <button onClick={goToPage}>Go to New Page</button>;}

在 React Router v5 中,useHistory hook 提供对 history 对象的访问,你可以使用它来 push 新路由。例如,history.push('/new-page') 将导航到指定路由。

例如:

jsx 复制代码
import { useHistory } from 'react-router-dom';
function MyComponent() {  const history = useHistory();  const goToPage = () => history.push('/new-page');  return <button onClick={goToPage}>Go to New Page</button>;}

两种方法都允许你在 React Router 中编程式导航。

74. 如何在 React 中实现路由守卫或私有路由?

要实现私有路由,创建一个在渲染目标路由之前检查用户是否已认证的组件。

示例:

jsx 复制代码
import { Navigate } from 'react-router-dom';
function PrivateRoute({ children }) {  return isAuthenticated ? children : <Navigate to="/login" />;}
  • PrivateRoute:检查认证状态,要么渲染 children(受保护路由),要么重定向到登录页面。
  • <Navigate>:替代 React Router v6+ 中已弃用的 <Redirect> 用于重定向。

75. 如何在多页 React 应用中管理活跃路由状态?

使用 useLocation hook 获取当前路由,并条件性地应用活跃状态的样式。

示例:

jsx 复制代码
import { useLocation } from 'react-router-dom';
function NavBar() {  const location = useLocation();  return (    <nav>      <ul>        <li className={location.pathname === '/home' ? 'active' : ''}>Home</li>        <li className={location.pathname === '/about' ? 'active' : ''}>          About        </li>      </ul>    </nav>  );}

76. 如何在 React Router 中处理 404 错误或页面未找到?

要在 React Router 中处理 404 错误或页面未找到,在路由配置末尾创建一个通配路由,渲染自定义 404 组件。

示例:

jsx 复制代码
import { Routes, Route } from 'react-router-dom';
function NotFound() {  return <h1>404 - Page Not Found</h1>;}
function App() {  return (    <Routes>      <Route path="/" element={<Home />} />      <Route path="/about" element={<About />} />      <Route path="*" element={<NotFound />} />    </Routes>  );}

在此示例中,当没有其他路由匹配 URL 时,NotFound 组件被渲染,表示 404 错误。

77. 如何在 React Router 中获取查询参数?

在 React Router v6 中,可以使用 useSearchParams hook 从 URL 中访问查询参数。

示例:

jsx 复制代码
import { useSearchParams } from 'react-router-dom';
function MyComponent() {  const [searchParams] = useSearchParams();  const queryParam = searchParams.get('paramName');  return <div>Query Param: {queryParam}</div>;}

此 hook 允许你在 React Router v6 中检索和操作查询参数。

78. 如何在 React Router 中登录后自动重定向?

要在 React Router 中登录后自动重定向,使用 useNavigate hook 在成功认证后导航到目标路由。

示例:

jsx 复制代码
import { useNavigate } from 'react-router-dom';
function Login() {  const navigate = useNavigate();
  const handleLogin = () => {        navigate('/dashboard');  };
  return (    <div>      <button onClick={handleLogin}>Login</button>    </div>  );}

在此示例中,handleLogin 函数在成功登录后导航到 /dashboard 路由。

79. 如何在 React Router 中向路由组件传递 props?

在 React Router v6 中,可以使用 <Route> 组件的 element prop 向路由组件传递 props。

示例:

jsx 复制代码
import { Routes, Route } from 'react-router-dom';
function MyComponent({ propValue }) {  return <div>Prop Value: {propValue}</div>;}
function App() {  return (    <Routes>      <Route path="/my-route" element={<MyComponent propValue="Hello" />} />    </Routes>  );}

在此示例中,渲染 /my-route 路由时将 propValue prop 传递给了 MyComponent 组件。

React 国际化

理解 React 中的国际化(i18n)在前端面试中很重要,因为许多产品服务全球受众,必须支持多种语言和区域设置。展示你可以实现 i18n 表明你关注无障碍访问、用户体验,并准备好为多样化用户构建应用。

80. 如何本地化 React 应用?

本地化通常涉及 react-i18next 或 react-intl 等库。为不同语言设置翻译文件,并使用提供的 hooks 或组件在应用中配置这些库。

jsx 复制代码
import { useTranslation } from 'react-i18next';
const MyComponent = () => {  const { t } = useTranslation();  return <p>{t('welcome_message')}</p>;};

查看详细解释并跟踪学习进度 ->

81. 什么是 react-intl

react-intl 是一个为 React 应用提供国际化(i18n)支持的库。它帮助格式化数字、日期、字符串,并处理翻译/本地化。它与 JavaScript 的 Intl API 集成,提供区域特定的数据和翻译管理。

82. react-intl 的主要特性有哪些?

  • 格式化文本:帮助使用占位符格式化消息和字符串。
  • 数字格式化:允许根据区域设置格式化数字、货币和百分比。
  • 日期和时间格式化:帮助根据区域设置以各种格式格式化日期和时间。
  • 复数和性别支持:提供复数形式和性别感知的字符串格式化。

83. react-intl 中有哪两种格式化方式?

  • 基于组件的格式化 :使用 React 组件如 <FormattedMessage /><FormattedNumber /><FormattedDate /> 等来格式化内容。
  • 基于 Hook 的格式化 :使用 useIntl 等 hooks 在组件内部以命令式方式格式化消息、数字或日期。

84. 如何在 react-intl 中使用 FormattedMessage 作为占位符?

你可以使用 FormattedMessage 组件来处理字符串中的占位符。占位符会被翻译后字符串中的变量动态替换。

jsx 复制代码
import { FormattedMessage } from 'react-intl';
function WelcomeMessage() {  return (    <FormattedMessage      id="welcome"      defaultMessage="Hello, {name}!"      values={{ name: 'John' }}    />  );}

这里 {name} 是一个占位符,John 会替换它。

85. 如何使用 React Intl 访问当前 locale?

你可以使用 useIntl hook 或 IntlProviderlocale prop 访问当前 locale。

使用 useIntl

jsx 复制代码
import { useIntl } from 'react-intl';
function LocaleDisplay() {  const intl = useIntl();  return <div>Current locale: {intl.locale}</div>;}

使用 IntlProvider

jsx 复制代码
<IntlProvider locale="en" messages={messages}>  <MyComponent /></IntlProvider>

这里 locale="en" 定义了当前 locale。

86. 如何使用 react-intl 格式化日期?

你可以使用 <FormattedDate /> 组件或 useIntl hook 的 formatDate 方法来格式化日期。

使用 <FormattedDate /> 组件:

jsx 复制代码
import { FormattedDate } from 'react-intl';
function DateComponent() {  return (    <FormattedDate      value={new Date()}      year="numeric"      month="long"      day="2-digit"    />  );}

使用 useIntl hook:

jsx 复制代码
import { useIntl } from 'react-intl';
function DateComponent() {  const intl = useIntl();  const formattedDate = intl.formatDate(new Date(), {    year: 'numeric',    month: 'long',    day: '2-digit',  });  return <div>{formattedDate}</div>;}

这些方法允许你以区域敏感的方式格式化日期。

React 测试

理解 React 中的测试在前端面试中很重要,因为这表明你可以编写可靠、可维护的代码,并通过单元测试、集成测试和 UI 测试及早发现 bug。熟练使用 Jest 和 React Testing Library 等工具表明你重视代码质量,能够在具有 CI/CD 工作流的团队环境中有效工作。

87. 如何测试 React 应用?

测试 React 应用可以使用 Jest 和 React Testing Library。Jest 作为测试框架,React Testing Library 提供以类似于用户交互的方式测试组件的工具。

查看详细解释并跟踪学习进度 ->

88. 什么是 Jest?它如何用于测试 React 应用?

Jest 是一个 JavaScript 测试框架,提供测试运行器、断言库和 mock 支持。由于其简洁性以及与 React Testing Library 等工具的集成,它通常用于测试 React 应用。

89. 什么是 React Testing Library?它如何用于测试 React 组件?

React Testing Library 是 React 的一个测试工具,帮助以类似于用户与应用交互的方式测试组件。它提供渲染组件、与组件交互以及对渲染输出进行断言的功能。

90. 如何使用 React Testing Library 测试 React 组件?

要使用 React Testing Library 测试 React 组件,你可以:

  1. 使用 render 渲染组件。
  2. 与组件交互(如点击按钮、输入文本)。
  3. 使用 getByTextqueryByRole 等查询对渲染输出进行断言。

示例:

jsx 复制代码
import { render, screen, fireEvent } from '@testing-library/react';import MyComponent from './MyComponent';
test('renders component', () => {  render(<MyComponent />);  const button = screen.getByRole('button');  fireEvent.click(button);  expect(screen.getByText('Clicked!')).toBeInTheDocument();});

在此示例中,测试渲染 MyComponent,点击按钮,并断言文本 'Clicked!' 存在。

91. 如何测试 React 组件中的异步代码?

要测试 React 组件中的异步代码,可以使用 async/await 配合 React Testing Library 的 waitFor 来处理数据获取或 API 调用等异步操作。

示例:

jsx 复制代码
import { render, screen, waitFor } from '@testing-library/react';import MyComponent from './MyComponent';
test('fetches data and renders it', async () => {  render(<MyComponent />);  await waitFor(() => {    expect(screen.getByText('Data loaded')).toBeInTheDocument();  });});

在此示例中,测试等待数据加载完毕后再断言文本 'Data loaded' 存在。

92. 如何在 React 组件测试中 mock API 调用?

要在 React 组件测试中 mock API 调用,可以使用 Jest 的 jest.mock 来 mock API 模块并返回 mock 数据。这允许你在不进行实际网络请求的情况下模拟 API 响应。

示例:

jsx 复制代码
import { render, screen } from '@testing-library/react';
jest.mock('./api', () => ({  fetchData: jest.fn(() => Promise.resolve('mocked data')),}));
import MyComponent from './MyComponent';
test('fetches data and renders it', async () => {  render(<MyComponent />);  expect(screen.getByText('Loading...')).toBeInTheDocument();  expect(await screen.findByText('mocked data')).toBeInTheDocument();});

在此示例中,api 模块中的 fetchData 函数被 mock 为返回 'mocked data' 用于测试目的。

93. 如何测试函数组件中的 React hooks?

使用 @testing-library/react 中的 renderHook 在测试中渲染 hook,然后调用 act 来驱动任何 state 更新。

javascript 复制代码
import { renderHook, act } from '@testing-library/react';import useCounter from './useCounter';
test('increments counter', () => {  const { result } = renderHook(() => useCounter());  act(() => {    result.current.increment();  });  expect(result.current.count).toBe(1);});

旧资料从 @testing-library/react-hooks 导入 renderHook。该包已被弃用并在 v13 中合并到 @testing-library/react;请使用上面展示的导入方式。

94. 如何测试 React 中的自定义 hooks?

方法与上述相同:使用 renderHook 渲染 hook 并对 result.current 进行断言。对于依赖 context 的 hooks(如 router 或 theme provider),传递 wrapper 选项。

jsx 复制代码
import { renderHook, act } from '@testing-library/react';import useCustomHook from './useCustomHook';
test('hook behavior', () => {  const { result } = renderHook(() => useCustomHook());  act(() => {    result.current.doSomething();  });  expect(result.current.value).toBe('expected value');});
const wrapper = ({ children }) => (  <MyProvider value="test">{children}</MyProvider>);const { result } = renderHook(() => useCustomHook(), { wrapper });

95. React 测试中的 Shallow Renderer 是什么?

Shallow rendering 仅渲染组件一层深度:其子组件不会被渲染,仅作为 React elements 被引用。目的是将测试中的组件与其子组件隔离开来。

有两种实现:react-test-renderer/shallow(低级 API)和 Enzyme 的 shallow()(围绕它的流行封装)。

jsx 复制代码
import { shallow } from 'enzyme';const wrapper = shallow(<Button label="Click Me" />);expect(wrapper.text()).toBe('Click Me');

不要在新代码中使用 shallow rendering。Enzyme 已无人维护,且没有官方的 React 17+ 适配器。React 文档推荐使用 React Testing Library,它以用户看到的方式渲染组件并基于可访问的输出进行断言,因此测试不会因内部重构而失效。改用 RTL 的 render 配合 screen.getByRole / getByText

96. React 中的 Snapshot Testing 是什么?

React 中的 Snapshot Testing 是一种测试技术,用于捕获组件的渲染输出并将其保存为快照。后续的测试运行会将当前输出与保存的快照进行比较,以检测任何意外变化。如果输出与快照不同,测试失败,表明组件的输出发生了变化。

以下是使用 Jest 进行 Snapshot Testing 的示例:

jsx 复制代码
import React from 'react';import renderer from 'react-test-renderer';import MyComponent from './MyComponent';
test('renders correctly', () => {  const tree = renderer.create(<MyComponent />).toJSON();  expect(tree).toMatchSnapshot();});

在此示例中,renderer.create 函数渲染 MyComponent 并将其转换为 JSON 树。toMatchSnapshot 函数保存组件输出的快照。后续测试运行将当前输出与保存的快照进行比较,确保组件的输出保持一致。

97. 如何测试使用 context 的 React 组件?

要测试使用 context 的 React 组件,可以用带有测试所需 context 值的 context provider 包裹组件。这允许你模拟 context 值并根据这些值测试组件的行为。

示例:

jsx 复制代码
import { render } from '@testing-library/react';import { MyContextProvider } from './MyContextProvider';import MyComponent from './MyComponent';
test('renders correctly with context', () => {  const { getByText } = render(    <MyContextProvider value="test value">      <MyComponent />    </MyContextProvider>,  );  expect(getByText('test value')).toBeInTheDocument();});

在此示例中,MyComponent 被包裹在带有特定 context 值的 MyContextProvider 中用于测试。测试验证组件使用提供的 context 值正确渲染。

98. 如何测试使用 Redux 的 React 组件?

要测试使用 Redux 的 React 组件,可以使用 redux-mock-store 库创建一个带有目标 state 的 mock store 用于测试。这允许你模拟 Redux store 并根据该 state 测试组件的行为。

示例:

jsx 复制代码
import { render } from '@testing-library/react';import configureStore from 'redux-mock-store';import { Provider } from 'react-redux';import MyComponent from './MyComponent';
const mockStore = configureStore([]);
test('renders correctly with Redux state', () => {  const store = mockStore({ counter: 0 });  const { getByText } = render(    <Provider store={store}>      <MyComponent />    </Provider>,  );  expect(getByText('Counter: 0')).toBeInTheDocument();});

在此示例中,MyComponent 被包裹在 Provider 中,带有一个包含初始 state { counter: 0 } 的 mock Redux store 用于测试。测试验证组件使用提供的 Redux state 正确渲染。

99. React 测试中 Shallow Rendering 和 Full DOM Rendering 的关键区别是什么?

  • Shallow Rendering:仅渲染被测试的组件,不渲染其子组件。适用于独立的单元测试。
  • Full DOM Rendering:挂载整个组件树,包括子组件,提供完整的 DOM 结构。适用于集成测试。

100. React 中的 TestRenderer 包是什么?

react-test-renderer 是一个将 React 组件渲染为纯 JS 对象树(而非 DOM)的工具,适用于无需浏览器环境的快照测试。

jsx 复制代码
import TestRenderer from 'react-test-renderer';import MyComponent from './MyComponent';
const renderer = TestRenderer.create(<MyComponent />);const tree = renderer.toJSON();expect(tree).toMatchSnapshot();

react-test-renderer 自 React 19 起已弃用,React 团队建议迁移。对于组件测试,使用 React Testing Library 配合 DOM 环境(Jest 使用 jsdom,Vitest 内置)。对于快照测试,序列化 RTL 的 render 产生的 DOM:

jsx 复制代码
import { render } from '@testing-library/react';const { container } = render(<MyComponent />);expect(container).toMatchSnapshot();

React 19 与现代 React

React 19 增加了 Actions 和表单集成、use hook、稳定的 Server Components 以及 React Compiler。这些是 2026 年常见的面试话题。

101. React 19 有哪些新特性?

React 19 增加:

  • Actions:包装异步工作的函数,通过新的 hooks 产生 pending/error/data 状态。
  • use hook:在渲染期间读取 Promise 和 context。
  • 稳定的 React Server ComponentsServer Actions
  • 原生支持 <form action={fn}>
  • 函数组件上 ref 作为常规 prop(不再需要 forwardRef)。
  • JSX 中 <title><meta> 和样式表的提升。
  • React Compiler:一个可选加入的构建时优化器,自动进行 memoization。

这些共同将数据变更和异步 UI 状态移入 React 本身,而不是让每个应用自行实现这些模式。

102. React 19 中的 Actions 是什么?

按照惯例,Action 是一个传递给 React API 的异步函数,React 在 transition 中运行它:useActionStatestartTransition(来自 useTransition)或 <form action={...}> prop。React 跟踪 pending 状态、暴露错误,并在 transition 中应用更新,使 UI 保持响应。这消除了切换 loading 标志、包裹 try/catch 以及分别管理 error 和 data 状态的常见模板代码。

jsx 复制代码
import { useActionState } from 'react';
async function updateName(prevState, formData) {  const name = formData.get('name');  const error = await saveName(name);  if (error) return { error };  return { name };}
function NameForm() {  const [state, dispatchAction, isPending] = useActionState(updateName, {    name: '',  });  return (    <form action={dispatchAction}>      <input name="name" defaultValue={state.name} />      <button disabled={isPending}>Save</button>      {state.error && <p>{state.error}</p>}    </form>  );}

103. useActionState hook 的作用是什么?

useActionState 接收一个 action 函数和一个初始 state,返回 [state, dispatchAction, isPending]。调用 dispatchAction(通常通过将其传递给 <form action>)会运行 action,将 isPending 标记为 true,并在 action resolve 后用返回值替换 state。一个 hook 覆盖了原本需要三个独立 useState 调用来分别管理 data、loading 和 error 的逻辑。

104. useOptimistic 的作用是什么?

useOptimistic 在 action 执行期间立即渲染乐观版本的 state,然后在 action 完成后自动恢复到真实 state。适用于聊天消息、点赞、列表重排序,或任何网络往返会感觉延迟的场景。

jsx 复制代码
import { useOptimistic } from 'react';
function MessageList({ messages, sendMessage }) {  const [optimisticMessages, addOptimistic] = useOptimistic(    messages,    (state, newMessage) => [...state, { text: newMessage, sending: true }],  );
  async function handleSend(formData) {    const text = formData.get('text');    addOptimistic(text);    await sendMessage(text);  }
  return (    <>      {optimisticMessages.map((m, i) => (        <p key={i} style={{ opacity: m.sending ? 0.5 : 1 }}>          {m.text}        </p>      ))}      <form action={handleSend}>        <input name="text" />      </form>    </>  );}

105. use hook 是什么?它与 useEffect + fetch 有何不同?

use 在渲染期间读取 Promise 或 Context 的值。传入 Promise 时,它会挂起(suspend)组件直到 Promise resolve(由最近的 <Suspense> 边界处理),然后返回 resolve 后的值。与 useEffect 不同,use 可以在条件语句和循环中调用,且 resolve 后的数据在挂起后同步可用,因此无需在组件树中传递 loading 状态。

jsx 复制代码
import { use, Suspense } from 'react';
function Profile({ userPromise }) {  const user = use(userPromise);   return <h1>{user.name}</h1>;}
async function Page() {  const userPromise = fetchUser();  return (    <Suspense fallback={<p>Loading...</p>}>      <Profile userPromise={userPromise} />    </Suspense>  );}

106. 什么是 React Server Components?

Server Components 在服务器上渲染,并将其输出流式传输到客户端。它们从不会向浏览器发送 JavaScript,可以直接 await 数据(无需 useEffect 往返),并可以访问仅限服务端的资源(如数据库或文件系统)。它们不能使用 useStateuseEffect 等 hooks,不能附加事件处理程序,也不能使用仅限浏览器的 API;这些仍然属于 Client Components(标记为 'use client' 的文件)。

107. Server Components 和 Client Components 有什么区别?

Server Component Client Component
运行位置 服务器(构建或请求时) 浏览器(hydration 后)
发送 JS
State / effects 不允许 允许
事件处理程序 不允许 允许
可直接 await 数据 否(使用 use 或在 effect 中 fetch)
可导入另一方 是(渲染 Client Components) 否(不能导入 Server Components,只能作为 props/children 接收)

Server Components 通常是获取数据的外层外壳;Client Components 是标记有 'use client' 的交互式叶子节点。

108. 什么是 React Compiler?

React Compiler 是一个可选加入的构建时工具,它分析你的组件并在安全的地方自动插入等效于 useMemouseCallbackReact.memo 的 memoization。它在大多数情况下消除了手动 memoization 的需求。它严格强制执行 Rules of React:如果你的代码违反规则(如修改 props、条件调用 hooks),编译器会跳过该组件而非产生不正确的输出。

109. useTransitionuseDeferredValue 有什么区别?

两者都将更新标记为非紧急,以便 React 保持 UI 响应。区别在于控制的位置:

  • useTransition:在调用点包装 state setter。由你决定某个特定更新何时应为 transition(如搜索提交)。
  • useDeferredValue:在消费点包装一个值。它返回该值的一个滞后副本,在紧急渲染之后更新,适用于数据源不在你控制范围内的情况(如通过 props 传入的值)。
jsx 复制代码
const [isPending, startTransition] = useTransition();startTransition(() => setQuery(input));
const deferredQuery = useDeferredValue(query);return <ExpensiveResults query={deferredQuery} />;

110. React 19 中新的 <form action> prop 如何工作?

React 19 允许你直接将函数传递给 <form action>(以及 <button formAction>)。当表单提交时,React 以 FormData 作为参数调用该函数,在 transition 中运行它,并在成功时重置非受控输入。将其与 useActionStateuseFormStatus 结合使用,无需手动的 onSubmit 管道即可获得 pending 状态和错误处理。

jsx 复制代码
import { useFormStatus } from 'react-dom';
function SubmitButton() {  const { pending } = useFormStatus();  return <button disabled={pending}>{pending ? 'Saving...' : 'Save'}</button>;}
function ProfileForm() {  async function save(formData) {    await updateProfile(Object.fromEntries(formData));  }  return (    <form action={save}>      <input name="name" />      <SubmitButton />    </form>  );}

结语

这 100+ 道题目应该能让你对 React 面试中会遇到什么有一个很好的了解。如果你正在寻找更深入的 React 面试准备资料,请参考以下资源:

你也可以探索 Top ReactJS Interview Questions 仓库 ------ 一个从真实面试经历中整理出的 50 道常见问题合集。

相关推荐
道里2 小时前
花了 5 万刀用 AI 写代码之后,这是我的全部经验
前端·人工智能
Royzst2 小时前
xml知识点
java·服务器·前端
IT_陈寒3 小时前
React useEffect闭包陷阱差点把我整失业了
前端·人工智能·后端
kyriewen3 小时前
推行AI写代码一年后,Code Review变成了新的加班理由
前端·ai编程·cursor
前端环境观察室4 小时前
给 Agent Browser Workflow 加一层可观测性:Trace、Snapshot 和 Review Queue
前端
柒瑞4 小时前
Superpowers结合Claude code浅实战
前端
Nian.Baikal4 小时前
从零搭建离线地图服务:Nginx + Cesium/Leaflet 实战指南
运维·前端·nginx
前端毕业班4 小时前
uniapp web 灵活控制 style scoped
前端·javascript·vue.js
lichenyang4534 小时前
鸿蒙业务需求实战:AI 问题走马灯卡片实现复盘
前端
ZTStory5 小时前
mise 一款可以在项目中独立管理语言、环境变量和任务的工具
前端·rust·命令行