MST

星途 面试题库

面试题:React 新旧生命周期对比及优化

React 16.3 引入了新的生命周期方法,如 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 等。请对比新旧生命周期方法,并阐述为什么要引入这些新方法?在实际项目中,如何利用这些新方法进行性能优化?
12.5万 热度难度
前端开发React

知识考点

AI 面试

面试题答案

一键面试

新旧生命周期方法对比

  1. 旧生命周期
    • componentWillMount:在挂载之前调用,此阶段可以进行一些初始化操作,如设置初始状态等。但由于在服务端渲染(SSR)中也会调用,且可能导致重复渲染,React 16.3 开始标记为不安全,后续版本将移除。
    • componentWillReceiveProps:在组件接收到新的 props 时调用,可根据新 props 更新 state。但它可能会导致一些意外的状态更新,因为即使 props 没有变化,某些情况下也可能被调用,React 16.3 开始标记为不安全,后续版本将移除。
    • componentWillUpdate:在组件即将更新时调用,可以在此进行一些准备工作。同样,在 React 16.3 开始标记为不安全,后续版本将移除。
  2. 新生命周期
    • getDerivedStateFromProps:静态方法,在组件挂载和更新时都会调用。它接收新的 props 和当前的 state,返回一个对象来更新 state,或者返回 null 表示不需要更新 state。它解决了 componentWillReceiveProps 可能导致的意外状态更新问题,强制将 props 到 state 的推导逻辑分离出来,使状态更新逻辑更清晰。
    • getSnapshotBeforeUpdate:在最近一次渲染输出(提交到 DOM 节点)之前调用。它可以返回一个值,这个值会作为 componentDidUpdate 的第三个参数传递。主要用于在 DOM 更新之前捕获一些信息,比如滚动位置等,方便在更新后基于这些信息做一些操作。

引入新方法的原因

  1. 解决现有生命周期方法的问题:旧的生命周期方法在 React 的异步渲染模式下存在一些隐患,例如 componentWillMountcomponentWillReceivePropscomponentWillUpdate 可能会在不恰当的时候被调用,导致意外的副作用和难以调试的问题。新的生命周期方法设计更清晰,能更好地适应 React 的未来架构。
  2. 适应异步渲染:React 16 引入了 Fiber 架构,支持异步渲染。新的生命周期方法能够更好地与异步渲染协同工作,确保在异步渲染过程中,组件的状态更新和副作用处理更加可靠和可控。

实际项目中利用新方法进行性能优化

  1. 使用 getDerivedStateFromProps 优化 state 更新
    • 避免不必要的 state 更新:通过仔细分析 props 和 state,在 getDerivedStateFromProps 中只在必要时返回新的 state。例如,一个根据父组件传递的 isLoading prop 来显示加载状态的组件:
    class MyComponent extends React.Component {
      state = {
        isLoading: false
      };
      static getDerivedStateFromProps(props, state) {
        if (props.isLoading!== state.isLoading) {
          return {
            isLoading: props.isLoading
          };
        }
        return null;
      }
      render() {
        return (
          <div>
            {this.state.isLoading? <p>Loading...</p> : <p>Loaded</p>}
          </div>
        );
      }
    }
    
    • 保持 state 与 props 的一致性:确保 state 是基于 props 推导出来的,这样可以避免 state 与 props 不一致导致的渲染问题,提高渲染的稳定性和可预测性。
  2. 使用 getSnapshotBeforeUpdate 优化 DOM 操作
    • 保存滚动位置:在列表组件更新时,如果有滚动条,为了保持用户的滚动位置,可以使用 getSnapshotBeforeUpdate
    class ScrollableList extends React.Component {
      constructor(props) {
        super(props);
        this.listRef = React.createRef();
      }
      getSnapshotBeforeUpdate(prevProps, prevState) {
        const list = this.listRef.current;
        return list.scrollTop;
      }
      componentDidUpdate(prevProps, prevState, snapshot) {
        const list = this.listRef.current;
        list.scrollTop = snapshot;
      }
      render() {
        return (
          <div ref={this.listRef}>
            {/* 列表内容 */}
          </div>
        );
      }
    }
    
    • 避免不必要的重排和重绘:通过在 DOM 更新前捕获必要信息,在更新后基于这些信息做操作,可以减少因为多次操作 DOM 导致的重排和重绘,提升性能。