MST

星途 面试题库

面试题:React状态更新的批处理机制在事件处理中的体现

在React应用中,有多个连续的状态更新操作在同一个事件处理函数内,例如一个表单提交事件中,同时更新用户名、密码等多个状态。但实际发现状态没有按照预期一次性更新,而是逐个更新。请分析原因,并阐述React的状态更新批处理机制,以及如何手动控制批处理以达到预期效果。
42.9万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

原因分析

在React中,状态更新是异步的。当在同一个事件处理函数内执行多个状态更新操作时,React会将这些更新操作合并,以提高性能。然而,React默认的批处理机制只在React自身的合成事件(如onClickonSubmit等)和生命周期函数中起作用。如果在这些之外(如原生DOM事件、setTimeoutPromise等回调中)更新状态,批处理机制不会生效,导致状态逐个更新。

React的状态更新批处理机制

  1. 批处理的概念:批处理是指React将多个状态更新操作合并成一次更新,从而减少不必要的渲染,提高性能。
  2. 何时批处理生效:在React合成事件和生命周期函数中,React会开启批处理模式。在这种模式下,多个setState(类组件)或setState(函数组件使用useState)操作会被合并,只触发一次重新渲染。
  3. 何时批处理不生效:在原生DOM事件处理函数、setTimeoutPromise等回调函数中,React不会自动开启批处理模式。此时,每一次状态更新都会立即触发重新渲染。

手动控制批处理以达到预期效果

  1. 使用unstable_batchedUpdates(React 18之前)
    • 在React 18之前,可以使用unstable_batchedUpdates函数手动开启批处理。这个函数接受一个回调函数作为参数,在回调函数内的所有状态更新都会被批处理。
    import React from 'react';
    import ReactDOM from'react-dom';
    
    const App = () => {
      const [count1, setCount1] = React.useState(0);
      const [count2, setCount2] = React.useState(0);
    
      const handleClick = () => {
        ReactDOM.unstable_batchedUpdates(() => {
          setCount1(count1 + 1);
          setCount2(count2 + 1);
        });
      };
    
      return (
        <div>
          <p>Count1: {count1}</p>
          <p>Count2: {count2}</p>
          <button onClick={handleClick}>Increment</button>
        </div>
      );
    };
    
    ReactDOM.render(<App />, document.getElementById('root'));
    
  2. React 18之后
    • 在React 18中,unstable_batchedUpdates已经被移除,取而代之的是自动批处理。无论在何种环境下(包括原生DOM事件、setTimeout等),React都会自动开启批处理模式。但如果需要在某些情况下禁用批处理,可以使用flushSync
    import React, { useState } from'react';
    import { flushSync } from'react-dom';
    
    const App = () => {
      const [count1, setCount1] = useState(0);
      const [count2, setCount2] = useState(0);
    
      const handleClick = () => {
        flushSync(() => {
          setCount1(count1 + 1);
        });
        setCount2(count2 + 1);
      };
    
      return (
        <div>
          <p>Count1: {count1}</p>
          <p>Count2: {count2}</p>
          <button onClick={handleClick}>Increment</button>
        </div>
      );
    };
    
    export default App;
    
    • flushSync会立即刷新React的状态更新,阻止批处理,确保在它内部的状态更新会立即生效,而不会与后续的状态更新合并。