Skip to content

[Fiber] PureComponent #8118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 27, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 35 additions & 12 deletions src/renderers/shared/fiber/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var {
} = require('ReactFiberUpdateQueue');
var { isMounted } = require('ReactFiberTreeReflection');
var ReactInstanceMap = require('ReactInstanceMap');
var shallowEqual = require('shallowEqual');

module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : PriorityLevel) => void) {

Expand Down Expand Up @@ -73,6 +74,28 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
},
};

function checkShouldComponentUpdate(workInProgress, oldProps, newProps, newState) {
const updateQueue = workInProgress.updateQueue;
if (oldProps === null || (updateQueue && updateQueue.isForced)) {
return true;
}

const instance = workInProgress.stateNode;
if (typeof instance.shouldComponentUpdate === 'function') {
return instance.shouldComponentUpdate(newProps, newState);
}

const type = workInProgress.type;
if (type.prototype && type.prototype.isPureReactComponent) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorta unfortunate if we need to check this every time. @sebmarkbage is there a place we could store this flag on the fiber?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured we start off with all of these being detected on demand and then we can create optimized paths for common patterns. E.g. for things that have shouldComponentUpdate, and things that are pure components and things that have no update life-cycles etc. by storing that in the tag like @acdlite said.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally the element itself could already have this information in its tag so that we don't even have to detect this during mount.

Copy link
Collaborator

@sebmarkbage sebmarkbage Oct 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, this can explode number of code paths so I figured we might want to optimize for collections of common patterns. For example: isPureComponent + no life-cycles. isPureComponent + only componentDidUpdate/componentWillUnmount but no other life-cycles. That way we can branch early and not do any of the checks. Never schedule update effects etc. EDIT: Although I guess it could also be a bitmask that allows this check to be optimized regardless of other early branches.

Copy link
Collaborator Author

@acdlite acdlite Oct 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the bitmask idea

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe let's wait on the bitmap thing for now until we know the layout we want for it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah I didn't mean I would add it now :D

return (
!shallowEqual(oldProps, newProps) ||
!shallowEqual(instance.state, newState)
);
}

return true;
}

function adoptClassInstance(workInProgress : Fiber, instance : any) : void {
instance.updater = updater;
workInProgress.stateNode = instance;
Expand Down Expand Up @@ -116,7 +139,6 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
// Called on a preexisting class instance. Returns false if a resumed render
// could be reused.
function resumeMountClassInstance(workInProgress : Fiber) : boolean {
const instance = workInProgress.stateNode;
let newState = workInProgress.memoizedState;
let newProps = workInProgress.pendingProps;
if (!newProps) {
Expand All @@ -132,13 +154,12 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
// componentWillMount and before this componentWillMount? Probably
// unsupported anyway.

const updateQueue = workInProgress.updateQueue;

// If this completed, we might be able to just reuse this instance.
if (typeof instance.shouldComponentUpdate === 'function' &&
!(updateQueue && updateQueue.isForced) &&
workInProgress.memoizedProps !== null &&
!instance.shouldComponentUpdate(newProps, newState)) {
if (!checkShouldComponentUpdate(
workInProgress,
workInProgress.memoizedProps,
newProps,
newState
)) {
return false;
}

Expand Down Expand Up @@ -196,10 +217,12 @@ module.exports = function(scheduleUpdate : (fiber: Fiber, priorityLevel : Priori
newState = previousState;
}

if (typeof instance.shouldComponentUpdate === 'function' &&
!(updateQueue && updateQueue.isForced) &&
oldProps !== null &&
!instance.shouldComponentUpdate(newProps, newState)) {
if (!checkShouldComponentUpdate(
workInProgress,
oldProps,
newProps,
newState
)) {
// TODO: Should this get the new props/state updated regardless?
return false;
}
Expand Down