zhangxl2929 / Notes

Notes for issues during work and learning
0 stars 0 forks source link

[React] 为什么组件的 props 明明变了组件却没有重新渲染 #3

Open zhangxl2929 opened 5 years ago

zhangxl2929 commented 5 years ago

开发中遇到一个问题,组件的 props 明明变了,在组件的 componentWillReceiveProps 中打了断点,程序走到这里了也确实 this.props.a !== nextProps.a 但是却没有重新渲染组件,根本没有走到组件的 render 函数中去。 很纳闷,一直的认知是当组件的 props 变了组件就会重新渲染,但是现在看起来似乎不尽然。 于是决定去看看源码。

zhangxl2929 commented 5 years ago

1. React.Component 的 shouldComponentUpdate 的行为

相关源码

function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextContext,
) {
  const instance = workInProgress.stateNode;
  // 如果组件中写了 shouldComponentUpdate 这个方法,那么就由这个方法判断是否需要重新渲染
  if (typeof instance.shouldComponentUpdate === 'function') {
    startPhaseTimer(workInProgress, 'shouldComponentUpdate');
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextContext,
    );
    stopPhaseTimer();

    if (__DEV__) {
      warningWithoutStack(
        shouldUpdate !== undefined,
        '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
          'boolean value. Make sure to return true or false.',
        getComponentName(ctor) || 'Component',
      );
    }

    return shouldUpdate;
  }

  // 如果继承自 PureReactComponent,则简单的比较前后两个 props 和 state
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    );
  }

  // 默认返回 true,即进行重新渲染
  return true;
}
zhangxl2929 commented 5 years ago

2. React.Component 的 对于 props 改变和重新渲染之间的关系到底是什么?

  1. componentWillReceiveProps 在什么时候会被调用

    源码

    // 实例没有 getDerivedStateFromProps 和 getSnapshotBeforeUpdate这两个方法
    const hasNewLifecycles =
    typeof getDerivedStateFromProps === 'function' ||
    typeof instance.getSnapshotBeforeUpdate === 'function';
    
    // Note: During these life-cycles, instance.props/instance.state are what
    // ever the previously attempted to render - not the "current". However,
    // during componentDidUpdate we pass the "current" props.
    
    // In order to support react-lifecycles-compat polyfilled components,
    // Unsafe lifecycles should not be invoked for components using the new APIs.
    if (
    !hasNewLifecycles &&
    (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
      typeof instance.componentWillReceiveProps === 'function')
    ) {
    // 前后 props 或者 context 不全等时调用
    if (oldProps !== newProps || oldContext !== nextContext) {
      callComponentWillReceiveProps(
        workInProgress,
        instance,
        newProps,
        nextContext,
      );
    }
    }