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 === null
Indicates that initialization needs to be calledconstructClassInstance
,mountClassInstance
Two functions- Otherwise, it indicates that the component is initialized
resumeMountClassInstance
Function 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:
constructClassInstance
This is primarily about initializing the component instance, calledconstructor
Constructor and injectclassComponentUpdater
mountClassInstance
It is calledgetDerivedStateFromProps
Life cycle function (V16) andUNSAFE_componentWillMount
Life 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 ⭐️