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

pure #13748

Merged
merged 2 commits into from Sep 27, 2018
Jump to file or symbol
Failed to load files and symbols.
+433 −70
Diff settings

Always

Just for now

@@ -37,6 +37,8 @@ import {
FunctionalComponentLazy,
ClassComponentLazy,
ForwardRefLazy,
PureComponent,
PureComponentLazy,
} from 'shared/ReactWorkTags';
import getComponentName from 'shared/getComponentName';

@@ -57,6 +59,7 @@ import {
REACT_CONTEXT_TYPE,
REACT_CONCURRENT_MODE_TYPE,
REACT_PLACEHOLDER_TYPE,
REACT_PURE_TYPE,
} from 'shared/ReactSymbols';

let hasBadMapPolyfill;
@@ -300,12 +303,14 @@ export function resolveLazyComponentTag(
return shouldConstruct(Component)
? ClassComponentLazy
: FunctionalComponentLazy;
} else if (
Component !== undefined &&
Component !== null &&
Component.$$typeof
) {
return ForwardRefLazy;
} else if (Component !== undefined && Component !== null) {
const $$typeof = Component.$$typeof;
if ($$typeof === REACT_FORWARD_REF_TYPE) {
return ForwardRefLazy;
}
if ($$typeof === REACT_PURE_TYPE) {
return PureComponentLazy;
}
}
return IndeterminateComponent;
}
@@ -363,15 +368,8 @@ export function createWorkInProgress(
}
}

// Don't touching the subtree's expiration time, which has not changed.
workInProgress.childExpirationTime = current.childExpirationTime;
if (pendingProps !== current.pendingProps) {

This comment has been minimized.

@sophiebits

sophiebits Sep 27, 2018

Member

What's the significance of this change?

This comment has been minimized.

@acdlite

acdlite Sep 27, 2018

Member

Previously the expirationTime field represented the pending priority of props, state, and context. In the begin phase, we check if this is lower priority than the render priority. If so, we can bail out.

After this change, it represents only the pending priority of state and context. The props check is moved to the begin phase. That way, in the begin phase, we can distinguish between an update caused by props versus an update caused by state or context. pure will never bail out if state or context has changed.

// This fiber has new props.
workInProgress.expirationTime = expirationTime;
} else {
// This fiber's props have not changed.
workInProgress.expirationTime = current.expirationTime;
}
workInProgress.expirationTime = current.expirationTime;

workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
@@ -460,6 +458,9 @@ export function createFiberFromElement(
case REACT_FORWARD_REF_TYPE:
fiberTag = ForwardRef;
break getTag;
case REACT_PURE_TYPE:
fiberTag = PureComponent;
break getTag;
default: {
if (typeof type.then === 'function') {
fiberTag = IndeterminateComponent;
@@ -31,6 +31,8 @@ import {
ContextConsumer,
Profiler,
PlaceholderComponent,
PureComponent,
PureComponentLazy,
} from 'shared/ReactWorkTags';
import {
NoEffect,
@@ -52,6 +54,7 @@ import {
enableSchedulerTracing,
} from 'shared/ReactFeatureFlags';
import invariant from 'shared/invariant';
import shallowEqual from 'shared/shallowEqual';
import getComponentName from 'shared/getComponentName';
import ReactStrictModeWarnings from './ReactStrictModeWarnings';
import warning from 'shared/warning';
@@ -196,6 +199,58 @@ function updateForwardRef(
return workInProgress.child;
}

function updatePureComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps: any,
updateExpirationTime,
renderExpirationTime: ExpirationTime,
) {
const render = Component.render;

if (
current !== null &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
) {
const prevProps = current.memoizedProps;
// Default to shallow comparison
let compare = Component.compare;
compare = compare !== null ? compare : shallowEqual;
if (compare(prevProps, nextProps)) {
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
}

// The rest is a fork of updateFunctionalComponent
let nextChildren;
prepareToReadContext(workInProgress, renderExpirationTime);
if (__DEV__) {
ReactCurrentOwner.current = workInProgress;
ReactCurrentFiber.setCurrentPhase('render');
nextChildren = render(nextProps);
ReactCurrentFiber.setCurrentPhase(null);
} else {
nextChildren = render(nextProps);
}

// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
memoizeProps(workInProgress, nextProps);
return workInProgress.child;
}

function updateFragment(
current: Fiber | null,
workInProgress: Fiber,
@@ -617,6 +672,7 @@ function mountIndeterminateComponent(
current,
workInProgress,
Component,
updateExpirationTime,
renderExpirationTime,
) {
invariant(
@@ -637,37 +693,53 @@ function mountIndeterminateComponent(
Component,
));
const resolvedProps = resolveDefaultProps(Component, props);
let child;
switch (resolvedTag) {
case FunctionalComponentLazy: {
return updateFunctionalComponent(
child = updateFunctionalComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
break;
}
case ClassComponentLazy: {
return updateClassComponent(
child = updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
break;
}
case ForwardRefLazy: {
return updateForwardRef(
child = updateForwardRef(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
break;
}
case PureComponentLazy: {
child = updatePureComponent(
current,
workInProgress,
Component,
resolvedProps,
updateExpirationTime,
renderExpirationTime,
);
break;
}
default: {
// This message intentionally doesn't metion ForwardRef because the
// fact that it's a separate type of work is an implementation detail.
// This message intentionally doesn't metion ForwardRef or PureComponent
// because the fact that it's a separate type of work is an
// implementation detail.
invariant(
false,
'Element type is invalid. Received a promise that resolves to: %s. ' +
@@ -676,6 +748,8 @@ function mountIndeterminateComponent(
);
}
}
workInProgress.memoizedProps = props;
return child;
}

const unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
@@ -1106,59 +1180,65 @@ function beginWork(
renderExpirationTime: ExpirationTime,
): Fiber | null {
const updateExpirationTime = workInProgress.expirationTime;
if (
!hasLegacyContextChanged() &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
) {
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
// in this optimized path, mostly pushing stuff onto the stack.
switch (workInProgress.tag) {
case HostRoot:
pushHostRootContext(workInProgress);
resetHydrationState();
break;
case HostComponent:
pushHostContext(workInProgress);
break;
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);

if (current !== null) {

This comment has been minimized.

@sophiebits

sophiebits Sep 27, 2018

Member

I don't understand the significance of these changes either.

This comment has been minimized.

@acdlite
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps === newProps &&
!hasLegacyContextChanged() &&
(updateExpirationTime === NoWork ||
updateExpirationTime > renderExpirationTime)
) {
// This fiber does not have any pending work. Bailout without entering
// the begin phase. There's still some bookkeeping we that needs to be done
// in this optimized path, mostly pushing stuff onto the stack.
switch (workInProgress.tag) {
case HostRoot:
pushHostRootContext(workInProgress);
resetHydrationState();
break;
case HostComponent:
pushHostContext(workInProgress);
break;
case ClassComponent: {
const Component = workInProgress.type;
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);
}
break;
}
break;
}
case ClassComponentLazy: {
const thenable = workInProgress.type;
const Component = getResultFromResolvedThenable(thenable);
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);
case ClassComponentLazy: {
const thenable = workInProgress.type;
const Component = getResultFromResolvedThenable(thenable);
if (isLegacyContextProvider(Component)) {
pushLegacyContextProvider(workInProgress);
}
break;
}
break;
}
case HostPortal:
pushHostContainer(
workInProgress,
workInProgress.stateNode.containerInfo,
);
break;
case ContextProvider: {
const newValue = workInProgress.memoizedProps.value;
pushProvider(workInProgress, newValue);
break;
}
case Profiler:
if (enableProfilerTimer) {
workInProgress.effectTag |= Update;
case HostPortal:
pushHostContainer(
workInProgress,
workInProgress.stateNode.containerInfo,
);
break;
case ContextProvider: {
const newValue = workInProgress.memoizedProps.value;
pushProvider(workInProgress, newValue);
break;
}
break;
case Profiler:
if (enableProfilerTimer) {
workInProgress.effectTag |= Update;
}
break;
}
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}
return bailoutOnAlreadyFinishedWork(
current,
workInProgress,
renderExpirationTime,
);
}

// Before entering the begin phase, clear the expiration time.
@@ -1171,6 +1251,7 @@ function beginWork(
current,
workInProgress,
Component,
updateExpirationTime,
renderExpirationTime,
);
}
@@ -1252,7 +1333,7 @@ function beginWork(
renderExpirationTime,
);
}
case ForwardRefLazy:
case ForwardRefLazy: {
const thenable = workInProgress.type;
const Component = getResultFromResolvedThenable(thenable);
const unresolvedProps = workInProgress.pendingProps;
@@ -1265,6 +1346,7 @@ function beginWork(
);
workInProgress.memoizedProps = unresolvedProps;
return child;
}
case Fragment:
return updateFragment(current, workInProgress, renderExpirationTime);
case Mode:
@@ -1283,6 +1365,32 @@ function beginWork(
workInProgress,
renderExpirationTime,
);
case PureComponent: {
const type = workInProgress.type;
return updatePureComponent(
current,
workInProgress,
type,
workInProgress.pendingProps,
updateExpirationTime,
renderExpirationTime,
);
}
case PureComponentLazy: {

This comment has been minimized.

@sophiebits

sophiebits Sep 27, 2018

Member

this duplication kills me a little bit :)

This comment has been minimized.

@acdlite

acdlite Sep 27, 2018

Member

Me too :D

const thenable = workInProgress.type;
const Component = getResultFromResolvedThenable(thenable);
const unresolvedProps = workInProgress.pendingProps;
const child = updatePureComponent(
current,
workInProgress,
Component,
resolveDefaultProps(Component, unresolvedProps),
updateExpirationTime,
renderExpirationTime,
);
workInProgress.memoizedProps = unresolvedProps;
return child;
}
default:
invariant(
false,
Oops, something went wrong.
ProTip! Use n and p to navigate between commits in a pull request.