Skip to content
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

Add withSuspenseConfig API #15593

Merged
merged 14 commits into from
May 16, 2019
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ ReactBatch.prototype.render = function(children: ReactNodeList) {
internalRoot,
null,
expirationTime,
null,
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This means that batches never suspend, but I'm not sure it makes sense for them to suspend anyway since they control the commit anyway. I'm not sure when the promise should resolve in this new multi-commit world.

work._onCommit,
);
return work;
Expand Down
1 change: 1 addition & 0 deletions packages/react-dom/src/fire/ReactFire.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ ReactBatch.prototype.render = function(children: ReactNodeList) {
internalRoot,
null,
expirationTime,
null,
work._onCommit,
);
return work;
Expand Down
28 changes: 22 additions & 6 deletions packages/react-reconciler/src/ReactFiberClassComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ import {
flushPassiveEffects,
} from './ReactFiberScheduler';
import {revertPassiveEffectsChange} from 'shared/ReactFeatureFlags';
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';

const fakeInternalInstance = {};
const isArray = Array.isArray;
Expand Down Expand Up @@ -184,9 +185,14 @@ const classComponentUpdater = {
enqueueSetState(inst, payload, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);

const update = createUpdate(expirationTime);
const update = createUpdate(expirationTime, suspenseConfig);
update.payload = payload;
if (callback !== undefined && callback !== null) {
if (__DEV__) {
Expand All @@ -204,9 +210,14 @@ const classComponentUpdater = {
enqueueReplaceState(inst, payload, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);

const update = createUpdate(expirationTime);
const update = createUpdate(expirationTime, suspenseConfig);
update.tag = ReplaceState;
update.payload = payload;

Expand All @@ -226,9 +237,14 @@ const classComponentUpdater = {
enqueueForceUpdate(inst, callback) {
const fiber = getInstance(inst);
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);

const update = createUpdate(expirationTime);
const update = createUpdate(expirationTime, suspenseConfig);
update.tag = ForceUpdate;

if (callback !== undefined && callback !== null) {
Expand Down
35 changes: 31 additions & 4 deletions packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import type {
} from './ReactFiberHostConfig';
import type {ReactEventComponentInstance} from 'shared/ReactTypes';
import type {SuspenseState} from './ReactFiberSuspenseComponent';
import type {SuspenseContext} from './ReactFiberSuspenseContext';

import {
IndeterminateComponent,
Expand Down Expand Up @@ -77,7 +78,12 @@ import {
getHostContext,
popHostContainer,
} from './ReactFiberHostContext';
import {popSuspenseContext} from './ReactFiberSuspenseContext';
import {
suspenseStackCursor,
InvisibleParentSuspenseContext,
hasSuspenseContext,
popSuspenseContext,
} from './ReactFiberSuspenseContext';
import {
isContextProvider as isLegacyContextProvider,
popContext as popLegacyContext,
Expand All @@ -94,7 +100,11 @@ import {
enableSuspenseServerRenderer,
enableEventAPI,
} from 'shared/ReactFeatureFlags';
import {markRenderEventTime, renderDidSuspend} from './ReactFiberScheduler';
import {
markRenderEventTimeAndConfig,
renderDidSuspend,
renderDidSuspendDelayIfPossible,
} from './ReactFiberScheduler';
import {getEventComponentHostChildrenCount} from './ReactFiberEvents';
import getComponentName from 'shared/getComponentName';
import warning from 'shared/warning';
Expand Down Expand Up @@ -698,7 +708,7 @@ function completeWork(
// was given a normal pri expiration time at the time it was shown.
const fallbackExpirationTime: ExpirationTime =
prevState.fallbackExpirationTime;
markRenderEventTime(fallbackExpirationTime);
markRenderEventTimeAndConfig(fallbackExpirationTime, null);

// Delete the fallback.
// TODO: Would it be better to store the fallback fragment on
Expand Down Expand Up @@ -727,7 +737,24 @@ function completeWork(
// in the concurrent tree already suspended during this render.
// This is a known bug.
if ((workInProgress.mode & BatchedMode) !== NoMode) {
renderDidSuspend();
const hasInvisibleChildContext =
current === null &&
workInProgress.memoizedProps.unstable_avoidThisFallback !== true;
if (
hasInvisibleChildContext ||
hasSuspenseContext(
suspenseStackCursor.current,
(InvisibleParentSuspenseContext: SuspenseContext),
)
) {
// If this was in an invisible tree or a new render, then showing
// this boundary is ok.
renderDidSuspend();
} else {
// Otherwise, we're going to have to hide content so we should
// suspend for longer if possible.
renderDidSuspendDelayIfPossible();
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions packages/react-reconciler/src/ReactFiberExpirationTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,18 @@ export function computeAsyncExpiration(
);
}

export function computeSuspenseExpiration(
currentTime: ExpirationTime,
timeoutMs: number,
): ExpirationTime {
// TODO: Should we warn if timeoutMs is lower than the normal pri expiration time?
return computeExpirationBucket(
currentTime,
timeoutMs,
LOW_PRIORITY_BATCH_SIZE,
);
}

// Same as computeAsyncExpiration but without the bucketing logic. This is
// used to compute timestamps instead of actual expiration times.
export function computeAsyncExpirationNoBucket(
Expand Down
19 changes: 16 additions & 3 deletions packages/react-reconciler/src/ReactFiberHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {SideEffectTag} from 'shared/ReactSideEffectTags';
import type {Fiber} from './ReactFiber';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {HookEffectTag} from './ReactHookEffectTags';
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';

import ReactSharedInternals from 'shared/ReactSharedInternals';

Expand All @@ -34,7 +35,7 @@ import {
flushPassiveEffects,
requestCurrentTime,
warnIfNotCurrentlyActingUpdatesInDev,
markRenderEventTime,
markRenderEventTimeAndConfig,
} from './ReactFiberScheduler';

import invariant from 'shared/invariant';
Expand All @@ -43,6 +44,7 @@ import getComponentName from 'shared/getComponentName';
import is from 'shared/objectIs';
import {markWorkInProgressReceivedUpdate} from './ReactFiberBeginWork';
import {revertPassiveEffectsChange} from 'shared/ReactFeatureFlags';
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';

const {ReactCurrentDispatcher} = ReactSharedInternals;

Expand Down Expand Up @@ -82,6 +84,7 @@ export type Dispatcher = {

type Update<S, A> = {
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
action: A,
eagerReducer: ((S, A) => S) | null,
eagerState: S | null,
Expand Down Expand Up @@ -728,7 +731,10 @@ function updateReducer<S, I, A>(
// TODO: We should skip this update if it was already committed but currently
// we have no way of detecting the difference between a committed and suspended
// update here.
markRenderEventTime(updateExpirationTime);
markRenderEventTimeAndConfig(
updateExpirationTime,
update.suspenseConfig,
);

// Process this update.
if (update.eagerReducer === reducer) {
Expand Down Expand Up @@ -1089,6 +1095,7 @@ function dispatchAction<S, A>(
didScheduleRenderPhaseUpdate = true;
const update: Update<S, A> = {
expirationTime: renderExpirationTime,
suspenseConfig: null,
action,
eagerReducer: null,
eagerState: null,
Expand All @@ -1114,10 +1121,16 @@ function dispatchAction<S, A>(
}

const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, fiber);
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
fiber,
suspenseConfig,
);

const update: Update<S, A> = {
expirationTime,
suspenseConfig,
action,
eagerReducer: null,
eagerState: null,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-reconciler/src/ReactFiberNewContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export function propagateContextChange(

if (fiber.tag === ClassComponent) {
// Schedule a force update on the work-in-progress.
const update = createUpdate(renderExpirationTime);
const update = createUpdate(renderExpirationTime, null);
update.tag = ForceUpdate;
// TODO: Because we don't have a work-in-progress, this will add the
// update to the current fiber, too, which means it will persist even if
Expand Down
22 changes: 19 additions & 3 deletions packages/react-reconciler/src/ReactFiberReconciler.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type {
} from './ReactFiberHostConfig';
import type {ReactNodeList} from 'shared/ReactTypes';
import type {ExpirationTime} from './ReactFiberExpirationTime';
import type {SuspenseConfig} from './ReactFiberSuspenseConfig';

import {
findCurrentHostFiber,
Expand Down Expand Up @@ -65,6 +66,7 @@ import {
import {StrictMode} from './ReactTypeOfMode';
import {Sync} from './ReactFiberExpirationTime';
import {revertPassiveEffectsChange} from 'shared/ReactFeatureFlags';
import {requestCurrentSuspenseConfig} from './ReactFiberSuspenseConfig';

type OpaqueRoot = FiberRoot;

Expand Down Expand Up @@ -117,6 +119,7 @@ function scheduleRootUpdate(
current: Fiber,
element: ReactNodeList,
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
callback: ?Function,
) {
if (__DEV__) {
Expand All @@ -137,7 +140,7 @@ function scheduleRootUpdate(
}
}

const update = createUpdate(expirationTime);
const update = createUpdate(expirationTime, suspenseConfig);
// Caution: React DevTools currently depends on this property
// being called "element".
update.payload = {element};
Expand Down Expand Up @@ -167,6 +170,7 @@ export function updateContainerAtExpirationTime(
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
expirationTime: ExpirationTime,
suspenseConfig: null | SuspenseConfig,
callback: ?Function,
) {
// TODO: If this is a nested container, this won't be the root.
Expand All @@ -191,7 +195,13 @@ export function updateContainerAtExpirationTime(
container.pendingContext = context;
}

return scheduleRootUpdate(current, element, expirationTime, callback);
return scheduleRootUpdate(
current,
element,
expirationTime,
suspenseConfig,
callback,
);
}

function findHostInstance(component: Object): PublicInstance | null {
Expand Down Expand Up @@ -291,12 +301,18 @@ export function updateContainer(
): ExpirationTime {
const current = container.current;
const currentTime = requestCurrentTime();
const expirationTime = computeExpirationForFiber(currentTime, current);
const suspenseConfig = requestCurrentSuspenseConfig();
const expirationTime = computeExpirationForFiber(
currentTime,
current,
suspenseConfig,
);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
expirationTime,
suspenseConfig,
callback,
);
}
Expand Down
Loading