The React repository master branch was pulled on September 12, 2018

React.PureComponent Official documentation: reactjs.org/docs/react-…

Difference between Component and PureComponent

React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.

React.PureComponent is almost the same as React.Component, The difference is that the react. PureComponent shallow compares whether the props and state have changed to determine whether to update the component.

React.purecomponent is another way to optimize React applications. We can also use shouldComponentUpdate to implement the same function. But using the React.PureComponent is much more intuitive and simple

Look at a simple example:

Use React.Com ponent

class CounterButton extends React.Component {
    state = {
        count: 1
    }
    shouldComponentUpdate(nextProps, nextState) {
        if (this.props.color ! == nextProps.color) {return true;
        }
        if (this.state.count ! == nextState.count) {return true;
        }
        return false;
    }
    render() {
        return (
            <button
                color={this.props.color}
                onClick={()= > this.setState(state => ({count: state.count + 1}))}>
                Count: {this.state.count}
            </button>); }}Copy the code

Use the React. PureComponent

class CounterButton extends React.PureComponent {
    state = {
        count: 1
    }
    render() {
        return (
            <button
                color={this.props.color}
                onClick={()= > this.setState(state => ({count: state.count + 1}))}>
                Count: {this.state.count}
            </button>); }}Copy the code

The above two pieces of code can avoid unnecessary component updates and optimize performance

The source code

Component & PureComponent definition

ReactBaseClasses.js

const emptyObject = {};
/** * Base class helpers for the updating state of a component. */
function Component(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};

Component.prototype.setState = function(partialState, callback) {
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

function ComponentDummy() {}
ComponentDummy.prototype = Component.prototype;

/** * Convenience component with default shallow equality check for sCU. */
function PureComponent(props, context, updater) {
  this.props = props;
  this.context = context;
  // If a component has string refs, we will assign a different object later.
  this.refs = emptyObject;
  this.updater = updater || ReactNoopUpdateQueue;
}

const pureComponentPrototype = (PureComponent.prototype = new ComponentDummy());
pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
Object.assign(pureComponentPrototype, Component.prototype);
pureComponentPrototype.isPureReactComponent = true;

export {Component, PureComponent};
Copy the code

Component is the same as PureComponent in the source code. The only difference is that PureComponent defines isPureReactComponent as true. This is to make it easier to distinguish between Component and PureComponent when the React application is running

The React Fiber framework for React16 provides an overview of how the React application is implemented

This article focuses on the call to the beginWork updateClassComponent in the Reconciliation phase.

The beginWork function has two main parts:

1. Process the Context

2. Call the update method based on the tag type of the Fiber node

The Fiber node with a ClassComponent tag calls the updateClassComponent function

function updateClassComponent(current: Fiber | null, workInProgress: Fiber, Component: any, nextProps, renderExpirationTime: ExpirationTime,) {... let shouldUpdate;if (current === null) {
    if (workInProgress.stateNode === null) {
      // In the initial pass we might need to construct the instance.
      constructClassInstance(
        workInProgress,
        Component,
        nextProps,
        renderExpirationTime,
      );
      mountClassInstance(
        workInProgress,
        Component,
        nextProps,
        renderExpirationTime,
      );
      shouldUpdate = true;
    } else {
      // In a resume, we'll already have an instance we can reuse.shouldUpdate = resumeMountClassInstance( workInProgress, Component, nextProps, renderExpirationTime, ); }}else {
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    );
  }
  return finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderExpirationTime,
  );
}
Copy the code

The execution process is as follows:

Current is null, indicating the first rendering of the current component

Determine whether the current component needs to be initialized

  • workInProgress.stateNode === nullIndicates that initialization needs to be calledconstructClassInstance,mountClassInstanceTwo functions
  • Otherwise, it indicates that the component is initializedresumeMountClassInstanceFunction reuses initialized instances

React16 React16 React16 React16 React16 React16 React16 React16 React16 React16 React16 React Fiber

Current is not null and is calledupdateClassInstance

ConstructClassInstance, mountClassInstance work done:

  • constructClassInstanceThis is primarily about initializing the component instance, calledconstructorConstructor and injectclassComponentUpdater
  • mountClassInstanceIt is calledgetDerivedStateFromPropsLife cycle function (V16) andUNSAFE_componentWillMountLife cycle function

The resumeMountClassInstance and updateClassInstance functions both assign the return value to the shouldUpdate variable, which is of Boolean type. Determines whether to execute the render function

Take the updateClassInstance function as an example to look at the source code

function updateClassInstance(current: Fiber, workInProgress: Fiber, ctor: any, newProps: any, renderExpirationTime: ExpirationTime,) :boolean {
  / / if the new and old props, will call UNSAFE_componentWillReceiveProps lifecycle function. let updateQueue = workInProgress.updateQueue;if(updateQueue ! = =null) {
    processUpdateQueue(
      workInProgress,
      updateQueue,
      newProps,
      instance,
      renderExpirationTime,
    );
    newState = workInProgress.memoizedState;
  }
  // Execute the getDerivedStateFromProps lifecycle function. const shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate( workInProgress, ctor, oldProps, newProps, oldState, newState, nextLegacyContext, );if (shouldUpdate) {
    ...
  } else{... }... return shouldUpdate; }Copy the code

Focus on checkShouldComponentUpdate function

function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextLegacyContext,) {
  const instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') {
    startPhaseTimer(workInProgress, 'shouldComponentUpdate');
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextLegacyContext,
    );
    stopPhaseTimer();

    return shouldUpdate;
  }

  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return(! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState) ); }return true;
}
Copy the code

The execution process is as follows:

ShouldComponentUpdate (); shouldComponentUpdate ();

2. Check whether this component is a PureComponent. If yes, execute shallowEqual to perform a shallow comparison between the new and old props and the new and old state, and return the comparison result

3. Return true by default

ShallowEqual function:

const hasOwnProperty = Object.prototype.hasOwnProperty;
function is(x, y) {
  // SameValue algorithm
  if (x === y) {
    // Steps 1-5, 7-10
    // Steps 6.b-6.e: +0 != -0
    // Added the nonzero y check to make Flow happy, but it is redundant
    returnx ! = =0|| y ! = =0 || 1 / x === 1 / y;
  } else {
    // Step 6.a: NaN == NaN
    returnx ! == x && y ! == y; }}/** * Performs equality by iterating through keys on an object and returning false * when any key has values which are not strictly equal between the arguments. * Returns true when the values of all keys are strictly equal. */
function shallowEqual(objA: mixed, objB: mixed) :boolean {
  if (is(objA, objB)) {
    return true;
  }
  if (
    typeofobjA ! = ='object' ||
    objA === null ||
    typeofobjB ! = ='object' ||
    objB === null
  ) {
    return false;
  }
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);
  if(keysA.length ! == keysB.length) {return false;
  }
  // Test for A's keys different from B.
  for (let i = 0; i < keysA.length; i++) {
    if(! hasOwnProperty.call(objB, keysA[i]) || ! is(objA[keysA[i]], objB[keysA[i]]) ) {return false; }}return true;
}

export default shallowEqual;
Copy the code

As you can see, shallowEqual is really shallow comparison, so using PureComponent for props and state is a complex data structure that often causes update problems

PureComponent is appropriate for components where props and state are simple data structures, or forceUpdate() is used to update complex data structures, or consider using immutable objects, or Component directly. Custom shouldComponentUpdate lifecycle function

The forceUpdate() function defines a component that is initialized with a classComponentUpdater. And call forceUpdate is called classComponentUpdater enqueueForceUpdate, to look at the definition

const classComponentUpdater = {
  ...
  enqueueForceUpdate(inst, callback) {
    ...
    const update = createUpdate(expirationTime);
    / /!!!!!!
    update.tag = ForceUpdate;

    if(callback ! = =undefined&& callback ! = =null) { update.callback = callback; } enqueueUpdate(fiber, update); scheduleWork(fiber, expirationTime); }};Copy the code

As you can see, update. Tag = ForceUpdate; This flag will be used later to indicate whether the update is ForceUpdate or not. The React Fiber framework is available in React16

We go back to updateClassInstance function, before the execution checkShouldComponentUpdate function, Performed processUpdateQueue function and the checkHasForceUpdateAfterProcessing function of judgment

The processUpdateQueue function essentially iterates through updateQueue and calls the getStateFromUpdate function

The getStateFromUpdate function looks like this:

function getStateFromUpdate<State> (workInProgress: Fiber, queue: UpdateQueue
       
        , update: Update
        
         , prevState: State, nextProps: any, instance: any,
        
       ) :any {
  switch (update.tag) {
    case ReplaceState: {
      ...
    }
    case CaptureUpdate: {
      ...
    }
    // Intentional fallthrough
    case UpdateState: {
      ...
    }
    case ForceUpdate: {
      hasForceUpdate = true;
      returnprevState; }}return prevState;
}
Copy the code

As you can see, this function determines the tag type of the update and sets the hasForceUpdate variable to true for the ForceUpdate type

CheckHasForceUpdateAfterProcessing function is returned hasForceUpdate variables, the code is as follows:

export function checkHasForceUpdateAfterProcessing() :boolean {
  return hasForceUpdate;
}
Copy the code

When the forceUpdate function is called, shouldComponentUpdate is mandatory regardless of whether the component is PureComponent and should be used with caution

Write in the last

React PureComponent is a component of React PureComponent. The React PureComponent is a component of React PureComponent

If you like my articles, go to my personal blog star ⭐️