Skip to content

Commit

Permalink
Offscreen add attach (#25603)
Browse files Browse the repository at this point in the history
`Offscreen.attach` is imperative API to signal to Offscreen that its
updates should be high priority and effects should be mounted. Coupled
with `Offscreen.detach` it gives ability to manually control Offscreen.
Unlike with mode `visible` and `hidden`, it is developers job to make
sure contents of Offscreen are not visible to users.
`Offscreen.attach` only works if mode is `manual`.

Example uses:
```jsx
let offscreenRef = useRef(null);
<Offscreen mode={'manual'} ref={offscreenRef)}>
  <Child />
</Offscreen>

// ------

// Offscreen is attached by default.
// For example user scrolls away and Offscreen subtree is not visible anymore.
offscreenRef.current.detach();

// User scrolls back and Offscreen subtree is visible again.
offscreenRef.current.attach();
```

Co-authored-by: Andrew Clark <git@andrewclark.io>

DiffTrain build for [996e4c0](996e4c0)
[View git log for this commit](https://github.com/facebook/react/commits/996e4c0d56dabab382ca932cd5b8517e63020999)
  • Loading branch information
sammy-SC committed Dec 12, 2022
1 parent a434b3e commit 806f024
Show file tree
Hide file tree
Showing 34 changed files with 1,880 additions and 1,370 deletions.
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b14d7fa4b88dad5f0017d084e462952c700aa2ad
996e4c0d56dabab382ca932cd5b8517e63020999
2 changes: 1 addition & 1 deletion compiled/facebook-www/REVISION_TRANSFORMS
Original file line number Diff line number Diff line change
@@ -1 +1 @@
b14d7fa4b88dad5f0017d084e462952c700aa2ad
996e4c0d56dabab382ca932cd5b8517e63020999
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-www-classic-b14d7fa4b-20221209";
var ReactVersion = "18.3.0-www-classic-996e4c0d5-20221212";

// ATTENTION
// When adding new symbols to this file,
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ if (
}
"use strict";

var ReactVersion = "18.3.0-www-modern-b14d7fa4b-20221209";
var ReactVersion = "18.3.0-www-modern-996e4c0d5-20221212";

// ATTENTION
// When adding new symbols to this file,
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-prod.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -643,4 +643,4 @@ exports.useSyncExternalStore = function(
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-www-classic-b14d7fa4b-20221209";
exports.version = "18.3.0-www-classic-996e4c0d5-20221212";
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-prod.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -635,4 +635,4 @@ exports.useSyncExternalStore = function(
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-www-modern-b14d7fa4b-20221209";
exports.version = "18.3.0-www-modern-996e4c0d5-20221212";
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-profiling.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,7 @@ exports.useSyncExternalStore = function(
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-www-classic-b14d7fa4b-20221209";
exports.version = "18.3.0-www-classic-996e4c0d5-20221212";

/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
Expand Down
2 changes: 1 addition & 1 deletion compiled/facebook-www/React-profiling.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ exports.useSyncExternalStore = function(
);
};
exports.useTransition = useTransition;
exports.version = "18.3.0-www-modern-b14d7fa4b-20221209";
exports.version = "18.3.0-www-modern-996e4c0d5-20221212";

/* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
if (
Expand Down
82 changes: 55 additions & 27 deletions compiled/facebook-www/ReactART-dev.classic.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}

var ReactVersion = "18.3.0-www-classic-b14d7fa4b-20221209";
var ReactVersion = "18.3.0-www-classic-996e4c0d5-20221212";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -2530,17 +2530,6 @@ function shim$1() {
var prepareScopeUpdate = shim$1;
var getInstanceFromScope = shim$1;

// Renderers that don't support microtasks
// can re-export everything from this module.
function shim$2() {
throw new Error(
"The current renderer does not support microtasks. " +
"This error is likely caused by a bug in React. " +
"Please file an issue."
);
} // Test selectors (when unsupported)
var scheduleMicrotask = shim$2;

var pooledTransform = new Transform();
var NO_CONTEXT = {};
var UPDATE_SIGNAL = {};
Expand Down Expand Up @@ -13106,13 +13095,15 @@ function updateSimpleMemoComponent(
function updateOffscreenComponent(current, workInProgress, renderLanes) {
var nextProps = workInProgress.pendingProps;
var nextChildren = nextProps.children;
var nextIsDetached =
(workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0;
var prevState = current !== null ? current.memoizedState : null;
markRef(current, workInProgress);

if (
nextProps.mode === "hidden" ||
nextProps.mode === "unstable-defer-without-hiding" || // TODO: remove read from stateNode.
workInProgress.stateNode._visibility & OffscreenDetached
nextProps.mode === "unstable-defer-without-hiding" ||
nextIsDetached
) {
// Rendering a hidden tree.
var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;
Expand Down Expand Up @@ -20800,22 +20791,46 @@ function getRetryCache(finishedWork) {
}

function detachOffscreenInstance(instance) {
var currentOffscreenFiber = instance._current;
var fiber = instance._current;

if (currentOffscreenFiber === null) {
if (fiber === null) {
throw new Error(
"Calling Offscreen.detach before instance handle has been set."
);
}

var executionContext = getExecutionContext();
if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
// The instance is already detached, this is a noop.
return;
} // TODO: There is an opportunity to optimise this by not entering commit phase
// and unmounting effects directly.

if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
scheduleMicrotask();
} else {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);

if (root !== null) {
instance._pendingVisibility |= OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
}
function attachOffscreenInstance(instance) {
var fiber = instance._current;

if (fiber === null) {
throw new Error(
"Calling Offscreen.detach before instance handle has been set."
);
}

if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
// The instance is already attached, this is a noop.
return;
}

var root = enqueueConcurrentRenderForLane(fiber, SyncLane);

if (root !== null) {
instance._pendingVisibility &= ~OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
}

Expand Down Expand Up @@ -21170,14 +21185,19 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
recursivelyTraverseMutationEffects(root, finishedWork);
}

commitReconciliationEffects(finishedWork); // TODO: Add explicit effect flag to set _current.
commitReconciliationEffects(finishedWork);
var offscreenInstance = finishedWork.stateNode; // TODO: Add explicit effect flag to set _current.

offscreenInstance._current = finishedWork; // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
// to support batching of `attach` and `detach` calls.

finishedWork.stateNode._current = finishedWork;
offscreenInstance._visibility &= ~OffscreenDetached;
offscreenInstance._visibility |=
offscreenInstance._pendingVisibility & OffscreenDetached;

if (flags & Visibility) {
var offscreenInstance = finishedWork.stateNode; // Track the current state on the Offscreen instance so we can
// Track the current state on the Offscreen instance so we can
// read it during an event

if (_isHidden) {
offscreenInstance._visibility &= ~OffscreenVisible;
} else {
Expand Down Expand Up @@ -27211,12 +27231,16 @@ function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
fiber.lanes = lanes;
var primaryChildInstance = {
_visibility: OffscreenVisible,
_pendingVisibility: OffscreenVisible,
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
_current: null,
detach: function() {
return detachOffscreenInstance(primaryChildInstance);
},
attach: function() {
return attachOffscreenInstance(primaryChildInstance);
}
};
fiber.stateNode = primaryChildInstance;
Expand All @@ -27230,12 +27254,16 @@ function createFiberFromLegacyHidden(pendingProps, mode, lanes, key) {

var instance = {
_visibility: OffscreenVisible,
_pendingVisibility: OffscreenVisible,
_pendingMarkers: null,
_transitions: null,
_retryCache: null,
_current: null,
detach: function() {
return detachOffscreenInstance(instance);
},
attach: function() {
return attachOffscreenInstance(instance);
}
};
fiber.stateNode = instance;
Expand Down
82 changes: 55 additions & 27 deletions compiled/facebook-www/ReactART-dev.modern.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}

var ReactVersion = "18.3.0-www-modern-b14d7fa4b-20221209";
var ReactVersion = "18.3.0-www-modern-996e4c0d5-20221212";

var LegacyRoot = 0;
var ConcurrentRoot = 1;
Expand Down Expand Up @@ -2527,17 +2527,6 @@ function shim$1() {
var prepareScopeUpdate = shim$1;
var getInstanceFromScope = shim$1;

// Renderers that don't support microtasks
// can re-export everything from this module.
function shim$2() {
throw new Error(
"The current renderer does not support microtasks. " +
"This error is likely caused by a bug in React. " +
"Please file an issue."
);
} // Test selectors (when unsupported)
var scheduleMicrotask = shim$2;

var pooledTransform = new Transform();
var NO_CONTEXT = {};
var UPDATE_SIGNAL = {};
Expand Down Expand Up @@ -12834,13 +12823,15 @@ function updateSimpleMemoComponent(
function updateOffscreenComponent(current, workInProgress, renderLanes) {
var nextProps = workInProgress.pendingProps;
var nextChildren = nextProps.children;
var nextIsDetached =
(workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0;
var prevState = current !== null ? current.memoizedState : null;
markRef(current, workInProgress);

if (
nextProps.mode === "hidden" ||
nextProps.mode === "unstable-defer-without-hiding" || // TODO: remove read from stateNode.
workInProgress.stateNode._visibility & OffscreenDetached
nextProps.mode === "unstable-defer-without-hiding" ||
nextIsDetached
) {
// Rendering a hidden tree.
var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;
Expand Down Expand Up @@ -20489,22 +20480,46 @@ function getRetryCache(finishedWork) {
}

function detachOffscreenInstance(instance) {
var currentOffscreenFiber = instance._current;
var fiber = instance._current;

if (currentOffscreenFiber === null) {
if (fiber === null) {
throw new Error(
"Calling Offscreen.detach before instance handle has been set."
);
}

var executionContext = getExecutionContext();
if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags) {
// The instance is already detached, this is a noop.
return;
} // TODO: There is an opportunity to optimise this by not entering commit phase
// and unmounting effects directly.

if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
scheduleMicrotask();
} else {
instance._visibility |= OffscreenDetached;
disappearLayoutEffects(currentOffscreenFiber);
disconnectPassiveEffect(currentOffscreenFiber);
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);

if (root !== null) {
instance._pendingVisibility |= OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
}
function attachOffscreenInstance(instance) {
var fiber = instance._current;

if (fiber === null) {
throw new Error(
"Calling Offscreen.detach before instance handle has been set."
);
}

if ((instance._pendingVisibility & OffscreenDetached) === NoFlags) {
// The instance is already attached, this is a noop.
return;
}

var root = enqueueConcurrentRenderForLane(fiber, SyncLane);

if (root !== null) {
instance._pendingVisibility &= ~OffscreenDetached;
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
}

Expand Down Expand Up @@ -20859,14 +20874,19 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
recursivelyTraverseMutationEffects(root, finishedWork);
}

commitReconciliationEffects(finishedWork); // TODO: Add explicit effect flag to set _current.
commitReconciliationEffects(finishedWork);
var offscreenInstance = finishedWork.stateNode; // TODO: Add explicit effect flag to set _current.

offscreenInstance._current = finishedWork; // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
// to support batching of `attach` and `detach` calls.

finishedWork.stateNode._current = finishedWork;
offscreenInstance._visibility &= ~OffscreenDetached;
offscreenInstance._visibility |=
offscreenInstance._pendingVisibility & OffscreenDetached;

if (flags & Visibility) {
var offscreenInstance = finishedWork.stateNode; // Track the current state on the Offscreen instance so we can
// Track the current state on the Offscreen instance so we can
// read it during an event

if (_isHidden) {
offscreenInstance._visibility &= ~OffscreenVisible;
} else {
Expand Down Expand Up @@ -26900,12 +26920,16 @@ function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
fiber.lanes = lanes;
var primaryChildInstance = {
_visibility: OffscreenVisible,
_pendingVisibility: OffscreenVisible,
_pendingMarkers: null,
_retryCache: null,
_transitions: null,
_current: null,
detach: function() {
return detachOffscreenInstance(primaryChildInstance);
},
attach: function() {
return attachOffscreenInstance(primaryChildInstance);
}
};
fiber.stateNode = primaryChildInstance;
Expand All @@ -26919,12 +26943,16 @@ function createFiberFromLegacyHidden(pendingProps, mode, lanes, key) {

var instance = {
_visibility: OffscreenVisible,
_pendingVisibility: OffscreenVisible,
_pendingMarkers: null,
_transitions: null,
_retryCache: null,
_current: null,
detach: function() {
return detachOffscreenInstance(instance);
},
attach: function() {
return attachOffscreenInstance(instance);
}
};
fiber.stateNode = instance;
Expand Down
Loading

0 comments on commit 806f024

Please sign in to comment.