React V16.8.6. See github.com/lxfriday/re…

Here is a Component created using the three components

import React, { Component, PureComponent } from 'react';
// function Component
function FuncComp() {
  return <div>FuncComp</div>
}
// Component
class Comp extends Component {
  render() {
    return (
      <div>
        component
      </div>); }}// Pure Component
class PureComp extends PureComponent {
  render() {
    return (
      <div>
        Pure Component
      </div>); }}console.log(<FuncComp />);
console.log(<Comp />);
console.log(<PureComp />);
export default class extends Component {
  render() {
    return (
      <div>
        <FuncComp />
        <Comp />
        <PureComp />
      </div>); }}Copy the code

Differences in generated elements

After React. CreateElement, the three components differ in type

Type is the function or class we just defined

__proto__ and prototype can’t understand can look at this article www.zhihu.com/question/34… __proto__ and prototype in js

Component


/** * 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.
  // ref can be created in a number of ways, except for strings, which are usually assigned to a variable by passing in a function
  // ref={el => this.el = el
  // There is another way to create a ref variable with react. createRef and use it this way
  // this.el = React.createRef()
  // ref={this.el}
  // For react. createRef, read reactcreateref.js
  this.refs = emptyObject;
  // We initialize the default updater but the real one gets injected by the
  // renderer.
  // If you print this in the component, you may have seen the updater property
  // ReactNoopUpdateQueue has very few apis and is basically useless for reporting warnings
  this.updater = updater || ReactNoopUpdateQueue;
}

Component.prototype.isReactComponent = {};

/** * Sets a subset of the state. Always use this to mutate * state. You should treat `this.state` as immutable. * * There is no guarantee that `this.state` will be immediately updated, so * accessing `this.state` after calling this method may return the old value. * * There is no guarantee that calls to `setState` will run synchronously, * as they may eventually be batched together. You can provide an optional * callback that will be executed when the call  to setState is actually * completed. * * When a function is provided to setState, it will be called at some point in * the future (not synchronously). It will be called with the up to date * component arguments (state, props, context). These values can be different * from this.* because your function may be called after receiveProps but before * shouldComponentUpdate, and this new state, props, and context will not yet be * assigned to this. * * @param {object|function} partialState Next partial state or function  to * produce next partial state to be merged with current state. * @param {? function} callback Called after state is updated. * @final * @protected */
// This is where we call setState in the component
// If you are not clear about the above comment and the corresponding document to look at the line
// At first, setState is a lot of logic, which ends up calling methods in updater
// Updater is an important thing
Component.prototype.setState = function (partialState, callback) {(function () {
    if(! (typeof partialState === 'object' || typeof partialState === 'function' || partialState == null)) {{throw ReactError('setState(...) : takes an object of state variables to update or a function which returns an object of state variables.'); }}}) ();this.updater.enqueueSetState(this, partialState, callback, 'setState');
};

/** * Forces an update. This should only be invoked when it is known with * certainty that we are **not** in a DOM transaction. * * You may want to call this when you know that some deeper aspect of the * component's state has changed but `setState` was not called. * * This will not invoke `shouldComponentUpdate`, but it will invoke * `componentWillUpdate` and `componentDidUpdate`. * * @param {? function} callback Called after update is complete. * @final * @protected */
Component.prototype.forceUpdate = function (callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

console.log('Component.prototype', Component.prototype);
Copy the code

PureComponent


PureComponent inherits from Component
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;
}

// PureComponent extends from Component
var pureComponentPrototype = PureComponent.prototype = new ComponentDummy();

console.log('pureComponentPrototype1', pureComponentPrototype);

pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
_assign(pureComponentPrototype, Component.prototype);
// Use this variable to distinguish between normal components
pureComponentPrototype.isPureReactComponent = true;

console.log('pureComponentPrototype2', pureComponentPrototype);
Copy the code

Results of three log prints:

The constructor object on the function’s Prototype property is not enumerable

pureComponentPrototype.constructor = PureComponent;
// Avoid an extra prototype jump for these methods.
_assign(pureComponentPrototype, Component.prototype);
Copy the code

After redirecting PureComponent to the constructor, the Component constructor does not override the PureComponent constructor when _assign copies the object attributes, as you can see in the following example.

The Component constructor is defined the same way as the PureComponeny constructor. PureComponent inherits from Component and copies the Component’s prototype methods

pureComponentPrototype.isPureReactComponent = true;
Copy the code

Represents the PureReactComponent, which plays a key role!!

React. CreateElement Generates the element flow

Why is createElement used

CreateElement (type, props, children) JSX code is changed to React. CreateElement (type, props, children)

Trace createElement to its source

Actual is derived by createElementWithValidation, its simplified code

function createElementWithValidation(type, props, children) {
  var validType = isValidElementType(type); // Is a valid reactElement

  var element = createElement.apply(this.arguments); // Call createElement generation

  // The result can be nullish if a mock or a custom function is used.
  // TODO: Drop this when these are no longer allowed as the type argument.
  if (element == null) {
    return element;
  }
  
  // A bunch of validation code in the middle...
  return element;
}
Copy the code

CreateElement simplified code


/** * Create and return a new ReactElement of the given type. * Return a new ReactElement of the given type https://reactjs.org/docs/react-api.html#createelement */
  export function createElement(type, config, children) {
  let propName;

  // Reserved names are extracted
  const props = {};

  let key = null;
  let ref = null;
  let self = null;
  let source = null;
  // Determine if the configuration is passed in, such as 
      
className is resolved into the configuration
if(config ! =null) { // Validate ref and key, only in development environment // Key and ref are props if (hasValidRef(config)) { ref = config.ref; } if (hasValidKey(config)) { key = ' ' + config.key; } // Assign // self to get this correctly later // source is basically useless, with some filename, line number, etc self = config.__self === undefined ? null : config.__self; source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object // go through the configuration and throw some built-in attributes to props //var RESERVED_PROPS = { // key: true, // ref: true, // __self: true, // __source: true // }; for (propName in config) { if( hasOwnProperty.call(config, propName) && ! RESERVED_PROPS.hasOwnProperty(propName) ) { props[propName] = config[propName]; }}}// Children can be more than one argument, and those are transferred onto // the newly allocated props object. // If children have only one parameter, they are assigned to the array const childrenLength = arguments.length - 2; if (childrenLength === 1) { props.children = children; } else if (childrenLength > 1) { const childArray = Array(childrenLength); for (let i = 0; i < childrenLength; i++) { childArray[i] = arguments[i + 2]; } if (__DEV__) { if (Object.freeze) { Object.freeze(childArray); } } props.children = childArray; } // Resolve default props // Check whether the component is powered with defaultProps. If yes, check whether the props is powered // If undefined, the default value will be set if (type && type.defaultProps) { const defaultProps = type.defaultProps; for (propName in defaultProps) { if (props[propName] === undefined) { props[propName] = defaultProps[propName]; }}}/ /... Warning not to access key and ref directly from props return ReactElement( type, key, ref, self, source, ReactCurrentOwner.current, props, ); } Copy the code

ReactElement


/** * Factory method to create a new React element. This no longer adheres to * the class pattern, so do not use new to call it. Also, no instanceof check * will work. Instead test ? typeof field against Symbol.for('react.element') to check * if something is a React Element. * * @param {*} type * @param {*} key * @param {string|object} ref * @param {*} self A *temporary* helper to detect places where `this` is * different from the `owner` when React.createElement is called, so that we * can warn. We want to get rid of owner and replace string `ref`s with arrow * functions, and as long as `this` and owner are the same, there will be no * change in behavior. Help detect this * @param {*} source An annotation object (added by a transpiler or otherwise) containing the file and line number * that was defined filename, line number, and/or other information. * @param {*} owner * @param {*} props * @internal */
// This is a factory function that helps us create React Element
// The internal code is very simple. Typeof helps us identify
// This is a React Element
var ReactElement = function (type, key, ref, self, source, owner, props) {
  var element = {
    // This tag allows us to uniquely identify this as a React Element
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner
  };

  {
    // The validation flag is currently mutative. We put it on
    // an external backing store so that we can freeze the whole object.
    // This can be replaced with a WeakMap once they are implemented in
    // commonly used development environments.
    element._store = {};

    // To make comparing ReactElements easier for testing purposes, we make
    // the validation flag non-enumerable (where possible, which should
    // include every environment we run tests in), so the test framework
    // ignores it.
    Object.defineProperty(element._store, 'validated', {
      configurable: false.enumerable: false.writable: true.value: false
    });
    // self and source are DEV only properties.
    Object.defineProperty(element, '_self', {
      configurable: false.enumerable: false.writable: false.value: self
    });
    // Two elements created in two different places should be considered
    // equal for testing purposes and therefore we hide it from enumeration.
    Object.defineProperty(element, '_source', {
      configurable: false.enumerable: false.writable: false.value: source
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element); }}return element;
};
Copy the code

We rely on ReactElement to eventually generate an element, so we see that the only difference in the initial generated element is Type, which refers to the component

 var element = {
    // This tag allows us to uniquely identify this as a React Element
    ?typeof: REACT_ELEMENT_TYPE,

    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,

    // Record the component responsible for creating this element.
    _owner: owner
  };
Copy the code

Differences in execution

In the react – dom. Development. Js, ctor. Prototype. IsPureReactComponent judgment to have the logo, there is PureComponent, will only to shallow props and the state

function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
  var instance = workInProgress.stateNode;
  if (typeof instance.shouldComponentUpdate === 'function') {
    startPhaseTimer(workInProgress, 'shouldComponentUpdate');
    varshouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext); stopPhaseTimer(); {! (shouldUpdate ! = =undefined)? warningWithoutStack$1(false.'%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(ctor) || 'Component') : void 0;
    }

    return shouldUpdate;
  }

  // Focus here
  // PureComponent.prototype.isPureReactComponent === true
  // PureComponent only makes shallow comparisons between props and state, and only references to objects
  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return! shallowEqual(oldProps, newProps) || ! shallowEqual(oldState, newState); }return true;
}
Copy the code

ShallowEqual for shallow comparison

PureComponent does not work scenarios

import React, { PureComponent } from 'react';
class PureComp extends PureComponent {
  constructor(props) {
    super(props);
    this.state = {
      userInfo: {
        name: 'lxfriday'.age: 24,},school: 'hzzz'}; } handleChangeUserInfo =(a)= > {
    const {
      userInfo,
    } = this.state;
    userInfo.sex = 'male';
    console.log('userInfo', userInfo);
    this.setState({ userInfo: userInfo });
  };
  handleChangeSchool = (a)= > {
    this.setState({ school: 'zzzh' });
  };
  render() {
    const {
      userInfo,
      school,
    } = this.state;
    return (
      <div>
        <button onClick={this.handleChangeUserInfo}>change userInfo</button>
        <button onClick={this.handleChangeSchool}>change school</button>
        <br />
        {JSON.stringify(userInfo)}
        <br />
        {school}
      </div>); }}console.log(<PureComp />);
export default PureComp;
Copy the code

Click the “change UserInfo” button, nothing changes on the page, but log prints out the value, school can change normally

Click on the former

After clicking on

Change PureComponent to Component and userInfo will change normally.

This is what PureComponent shallow alignments are all about. It doesn’t matter if the objects are the same in depth, and you can get a huge performance boost if you know that setState properties really only need shallow alignments to implement preset functions. If you change a property in a nested object, the result may be more than expected!!


AD time

Welcome attention, daily progress!!