React 常见问题

FreeGuideOnline 最新 2026-07-15

jsx // 传统方式(命令式) const btn = document.getElementById('btn'); btn.addEventListener('click', () => { document.getElementById('text').innerText = '已点击'; });

// React 方式(声明式) function App() { const [clicked, setClicked] = useState(false); return (

<button onClick={() => setClicked(true)}>点击

{clicked ? '已点击' : '未点击'}

); }


#### 虚拟 DOM 是什么?它是如何提升性能的?

虚拟 DOM(Virtual DOM)是真实 DOM 的轻量级 JavaScript 对象表示。React 会在内存中维护一棵虚拟 DOM 树,当状态改变时,先计算出新的虚拟 DOM 树,然后与旧的树进行对比(Diff 算法),最终只将真正变化的部分更新到真实 DOM 中。这避免了直接操作 DOM 的高昂开销,使得 UI 更新更高效。

#### 为什么 JSX 里写 HTML 不会报错?JSX 到底是什么?

JSX 是一种 JavaScript 的语法扩展,看起来像在 JS 里写 HTML。实际上,JSX 会被 Babel 等编译器转换成 `React.createElement()` 函数调用,最终生成 JavaScript 对象(虚拟 DOM 节点)。所以浏览器并不会直接执行 JSX,它只是开发阶段的语法糖。

```jsx
const element = <h1 className="greeting">Hello!</h1>;
// 编译后:
const element = React.createElement('h1', { className: 'greeting' }, 'Hello!');

组件与数据篇

函数组件和类组件有什么区别?现在该用哪种?

函数组件是使用 JavaScript 函数定义的组件,类组件则是使用 ES6 的 class 定义并继承 React.Component。在 React 16.8 引入 Hooks 之后,函数组件也能拥有状态和生命周期逻辑,而类组件逐渐成为历史遗留方式。官方推荐使用 函数组件 + Hooks 编写新代码,因为它更简洁、更易测试和拆分逻辑。

// 函数组件(推荐)
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

state 和 props 有什么区别?什么时候用哪个?

  • props(属性):由父组件传递给子组件,是只读的,子组件不能修改接收到的 props。
  • state(状态):组件内部私有的数据,可以修改,通过 useState Hook 或类组件的 this.setState 更新。

记住一个原则:数据属于哪个组件,就应该放在哪个组件的 state 里,然后通过 props 向下传递。如果多个组件需要共享数据,应该将状态提升到最近的公共祖先。

如何正确更新 state?为什么直接修改 state 不生效?

在函数组件中,必须使用 useState 返回的更新函数,例如 setCount(count + 1)。类组件中需使用 this.setState。直接给 state 赋值是无效的,因为 React 需要知道数据变化才能触发重新渲染。同时更新函数可能是异步的,如果新值依赖于旧值,应该使用函数式更新:

// 错误
count = 5;

// 正确
setCount(5);
// 依赖旧值时
setCount(prevCount => prevCount + 1);

Hooks 高频问题

useState 的初始值如何惰性计算?

如果 useState 的初始值需要复杂计算,可以传入一个函数,该函数只会在初次渲染时执行一次。

const [data, setData] = useState(() => {
  // 只在初始化时执行
  return expensiveComputation();
});

useEffect 何时执行?为什么会出现无限循环?

useEffect 默认在每次渲染后执行。如果你在 effect 内部更新了 effect 所依赖的状态,就会导致“渲染 → 执行 effect → 更新状态 → 再渲染”的死循环。解决方法是正确设置依赖数组,只让 effect 在指定值变化时执行。

useEffect(() => {
  // 错误:没有依赖数组,每次渲染都执行,且内部更新了 count → 无限循环
  setCount(count + 1);
});

// 正确:空数组表示只在挂载时执行一次
useEffect(() => {
  // 获取初始数据
}, []);

useMemo 和 useCallback 到底解决什么问题?

它们都用于性能优化,避免不必要的重新计算或函数重新创建。

  • useMemo:缓存计算结果,当依赖项未变时直接返回上次的计算值。
  • useCallback:缓存函数引用,避免因函数引用变化导致子组件无意义重渲染。

通常只在计算开销大、且依赖稳定的场景下使用,不要过早地滥用它们。

自定义 Hook 如何抽取复用逻辑?

自定义 Hook 就是一个以 use 开头的函数,内部可以调用其他 Hook。它可以把组件逻辑抽取成可复用的函数,例如处理表单输入、监听窗口尺寸等。

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  return width;
}