MST

星途 面试题库

面试题:React State更新机制及性能优化

在React应用中,我们知道状态更新可能会引发不必要的重新渲染。请阐述React的状态更新机制,以及如何利用shouldComponentUpdate、React.memo、useMemo、useCallback等手段来优化组件因状态更新导致的性能问题。假设我们有一个父组件Parent,包含子组件Child,Parent中有一个频繁变化的state,而Child仅依赖于Parent的部分state,如何避免Child不必要的重新渲染?
31.8万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

React的状态更新机制

  1. 单向数据流:在React中,数据是单向流动的,从父组件流向子组件。状态(state)通常在父组件中定义,然后通过props传递给子组件。
  2. 状态合并:当调用setState(类组件)或dispatch(使用useReducer的函数组件)时,React会将新的状态与当前状态合并,然后触发重新渲染。
  3. 批处理:React会批量处理状态更新,以减少不必要的重新渲染。例如,在一个事件处理函数中多次调用setState,React会将这些更新合并,最后只触发一次重新渲染。但在异步操作(如setTimeoutPromise等)中,React不会自动批处理,需要手动使用unstable_batchedUpdates(React 18之前)或React自动批处理机制(React 18及之后)。

优化手段

  1. shouldComponentUpdate:这是类组件中的生命周期方法。它接收nextPropsnextState作为参数,返回一个布尔值。如果返回true,则组件会重新渲染;返回false,则阻止组件重新渲染。可以通过比较nextPropscurrentPropsnextStatecurrentState来决定是否需要重新渲染。例如:
class Child extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.someProp!== this.props.someProp;
  }
  render() {
    return <div>{this.props.someProp}</div>;
  }
}
  1. React.memo:这是用于函数组件的高阶组件。它会对组件的props进行浅比较,如果props没有变化,就不会重新渲染组件。相当于类组件中的shouldComponentUpdate的默认浅比较行为。例如:
const Child = React.memo((props) => {
  return <div>{props.someProp}</div>;
});
  1. useMemo:用于缓存计算结果。它接收两个参数,一个是计算函数,另一个是依赖数组。只有当依赖数组中的值发生变化时,才会重新计算并返回新的值。可以用来缓存对象、数组等复杂数据结构,避免不必要的重新渲染。例如:
const Parent = () => {
  const [count, setCount] = useState(0);
  const complexData = useMemo(() => {
    // 复杂计算
    return { key: 'value' };
  }, []);
  return <Child data={complexData} />;
};
  1. useCallback:用于缓存函数。它接收两个参数,一个是回调函数,另一个是依赖数组。只有当依赖数组中的值发生变化时,才会返回新的函数。常用于将回调函数传递给子组件,避免子组件因父组件重新渲染而导致不必要的重新渲染。例如:
const Parent = () => {
  const [count, setCount] = useState(0);
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);
  return <Child onClick={handleClick} />;
};

避免Child不必要的重新渲染

  1. 使用React.memo:由于Child仅依赖于Parent的部分state,将Child组件包裹在React.memo中,这样当Parent重新渲染但传递给Child的props没有变化时,Child不会重新渲染。
const Child = React.memo((props) => {
  return <div>{props.specificState}</div>;
});

const Parent = () => {
  const [frequentState, setFrequentState] = useState(0);
  const [specificState, setSpecificState] = useState('initial value');
  return <Child specificState={specificState} />;
};
  1. 分离状态:在Parent组件中,将Child依赖的state单独分离出来,这样可以减少因频繁变化的state导致的Child重新渲染。例如,如果频繁变化的state是count,而Child依赖name,可以将name单独管理。
const Child = React.memo((props) => {
  return <div>{props.name}</div>;
});

const Parent = () => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('John');
  return <Child name={name} />;
};
  1. 使用useMemo和useCallback:如果Child依赖的props是通过计算得到的或者是函数,使用useMemouseCallback来缓存计算结果和函数,避免不必要的重新渲染。例如:
const Child = React.memo((props) => {
  return <div>{props.calculatedValue}</div>;
});

const Parent = () => {
  const [count, setCount] = useState(0);
  const calculatedValue = useMemo(() => {
    // 复杂计算
    return count * 2;
  }, [count]);
  return <Child calculatedValue={calculatedValue} />;
};