How to use renderDidSuspendDelayIfPossible method in Playwright Internal

Best JavaScript code snippet using playwright-internal

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

FiberCompleteWork.js

Source: FiberCompleteWork.js Github

copy
1import {
2  LazyComponent,
3  FunctionComponent,
4  SuspenseComponent,
5  HostText,
6  HostComponent,
7  HostRoot,
8  NoLanes,
9  NoFlags,
10  DidCapture,
11  OffscreenComponent,
12  OffscreenLane,
13  Update,
14  StaticMask,
15} from '@Jeact/shared/Constants';
16import {
17  popHostContainer,
18} from '@Jeact/vDOM/FiberHostContext';
19import {
20  mergeLanes,
21  includesSomeLane,
22} from '@Jeact/vDOM/FiberLane';
23import { 
24  createTextNode, 
25  createElement,
26  setInitialDOMProperties,
27  diffProperties,
28} from '@Jeact/vDOM/DOMComponent';
29import {
30  precacheFiberNode,
31  updateFiberProps
32} from '@Jeact/vDOM/DOMComponentTree';
33import {
34  suspenseStackCursor,
35  InvisibleParentSuspenseContext,
36  hasSuspenseContext
37} from '@Jeact/vDOM/FiberSuspenseContext';
38import {pop} from '@Jeact/vDOM/FiberStack';
39import {
40  renderDidSuspend,
41  renderDidSuspendDelayIfPossible,
42  popRenderLanes,
43  subtreeRenderLanes,
44} from '@Jeact/vDOM/FiberWorkLoop';
45
46function markUpdate(workInProgress){
47  workInProgress.flags |= Update;
48}
49
50function updateHostComponent(
51  current, 
52  workInProgress,
53  type,
54  newProps,
55){
56  const oldProps = current.memoizedProps;  
57  const instance = workInProgress.stateNode;
58  const updatePayload = diffProperties(
59    instance,
60    type,
61    oldProps,
62    newProps
63  )
64  workInProgress.updateQueue = updatePayload;
65  if (updatePayload){
66    markUpdate(workInProgress);
67  }
68}
69
70function appendAllChildren(parent, workInProgress){
71  let node = workInProgress.child;
72  while (node!==null){
73    if (node.tag === HostComponent || node.tag === HostText){
74      let domInstance = node.stateNode;
75      parent.appendChild(domInstance);
76    } else if (node.child !== null){
77      /*  <DOM>
78       *       <Function Component > <- childFiber
79       *          <Dom></Dom>
80       *       </Function Component>
81       *  </DOM>
82       */
83      node = node.child;
84      continue;
85    }
86    if (node === workInProgress) {
87      return;
88    }
89    while (node.sibling === null){
90      // when node's parent has siblings
91      if (node.return === workInProgress){
92        return;
93      }
94      node = node.return;
95    }
96    node.sibling.return = node.return;
97    node = node.sibling;
98  }
99};
100
101function bubbleProperties(completedWork){
102  const didBailout = completedWork.alternate !== null && 
103    completedWork.alternate.child === completedWork.child;
104
105    let newChildLanes = NoLanes;
106    let subtreeFlags = NoFlags;
107
108  if (!didBailout){
109    let child = completedWork.child;
110    while (child !== null){
111      newChildLanes = mergeLanes(
112        newChildLanes,
113        mergeLanes(child.lanes, child.childLanes),
114      );
115
116      subtreeFlags |= child.subtreeFlags;
117      subtreeFlags |= child.flags;
118
119      child = child.sibling;
120    }
121    completedWork.subtreeFlags |= subtreeFlags;
122  } else {
123    let child = completedWork.child;
124    while (child !== null){
125      newChildLanes = mergeLanes(
126        newChildLanes,
127        mergeLanes(child.lanes, child.childLanes),
128      );
129
130      subtreeFlags |= child.subtreeFlags & StaticMask;
131      subtreeFlags |= child.flags & StaticMask;
132      // child.return = completedWork;
133      child = child.sibling;
134    }
135    completedWork.subtreeFlags |= subtreeFlags;
136  }
137
138  completedWork.childLanes = newChildLanes;
139}
140
141export function completeWork(current, workInProgress,renderLanes){
142  const newProps = workInProgress.pendingProps;
143
144  switch(workInProgress.tag){
145    case HostText: {
146      const current = workInProgress.alternate;
147      if (current !== null && workInProgress.stateNode !== null){
148        // Update text node
149        const oldProps = current.memoizedProps;
150        // updateHostText: Compare text between new and old.
151        if (oldProps !== newProps){
152          markUpdate(workInProgress);
153        }
154      } else {
155        // Mount text node
156        const instance = createTextNode(
157          newProps
158        );      
159        workInProgress.stateNode = instance;
160        precacheFiberNode(workInProgress, instance)
161      }
162
163      bubbleProperties(workInProgress);
164      return null;
165    }
166    case HostComponent:{
167      const type = workInProgress.type;
168      if(current !== null && workInProgress.stateNode !== null){
169        updateHostComponent(
170          current,
171          workInProgress,
172          type,
173          newProps
174        )
175      } else {
176        if(!newProps){
177          bubbleProperties(workInProgress);
178          return null;
179        }
180
181        const instance = createElement(
182          type,
183        );
184        
185        precacheFiberNode(workInProgress, instance);
186        updateFiberProps(instance, newProps);
187
188        appendAllChildren(instance, workInProgress);
189        setInitialDOMProperties(instance, workInProgress) 
190        
191        workInProgress.stateNode = instance;
192      }
193      bubbleProperties(workInProgress);
194      return null;
195    }
196    case FunctionComponent:
197      bubbleProperties(workInProgress);
198      return null;
199    case HostRoot:{
200      const fiberRoot = workInProgress.stateNode;
201      popHostContainer(workInProgress);
202      bubbleProperties(workInProgress);
203      return null;
204    }
205    case LazyComponent:
206    case SuspenseComponent:{
207      pop(suspenseStackCursor);
208      const nextState = workInProgress.memoizedState;
209      
210      const nextDidTimeout = nextState !== null;
211      let prevDidTimeout = false;
212      if(current !== null){
213        const prevState = current.memoizedState;
214        prevDidTimeout = prevState !== null;
215      } 
216
217      if(nextDidTimeout && !prevDidTimeout){
218          const hasInvisibleChildContext = 
219            current === null &&
220            workInProgress.memoizedProps.avoid !== true;
221          if(
222              hasInvisibleChildContext ||
223              hasSuspenseContext(
224                suspenseStackCursor.current,
225                (InvisibleParentSuspenseContext)
226              )
227            ){
228            renderDidSuspend()
229          } else {
230            renderDidSuspendDelayIfPossible();
231          }
232      }
233      const wakeables = workInProgress.updateQueue;
234      if (wakeables !== null){
235        // If this boundary just timed out, schedule an effect to attach a 
236        // retry listener to the promise. We also use this flag to toggle 
237        // children.
238        workInProgress.flags |= Update;
239      }
240      bubbleProperties(workInProgress);
241      return null;
242    }
243    case OffscreenComponent:{
244      popRenderLanes(workInProgress);
245      const nextState = workInProgress.memoizedState;
246      const nextIsHidden = nextState !== null;
247
248      if(current !== null){
249        const prevState = current.memoizedState;
250        const prevIsHidden = prevState !== null;
251        if (prevIsHidden !== nextIsHidden){
252          workInProgress.flags |= Update;
253        }
254      }
255
256      // Don't bubble properties for hidden children.
257      if (
258        !nextIsHidden ||
259        includesSomeLane(subtreeRenderLanes, OffscreenLane)
260        ){
261        bubbleProperties(workInProgress);
262      }
263
264      return null;
265    }
266    default:
267      console.error('completeWork', workInProgress)
268  }
269}
270
Full Screen

ReactFiberThrow.old.js

Source: ReactFiberThrow.old.js Github

copy
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10import type {Fiber} from './ReactInternalTypes';
11import type {FiberRoot} from './ReactInternalTypes';
12import type {Lane, Lanes} from './ReactFiberLane.old';
13import type {CapturedValue} from './ReactCapturedValue';
14import type {Update} from './ReactUpdateQueue.old';
15import type {Wakeable} from 'shared/ReactTypes';
16import type {SuspenseContext} from './ReactFiberSuspenseContext.old';
17
18import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
19import {
20  ClassComponent,
21  HostRoot,
22  SuspenseComponent,
23  IncompleteClassComponent,
24  FunctionComponent,
25  ForwardRef,
26  SimpleMemoComponent,
27} from './ReactWorkTags';
28import {
29  DidCapture,
30  Incomplete,
31  NoFlags,
32  ShouldCapture,
33  LifecycleEffectMask,
34  ForceUpdateForLegacySuspense,
35  ForceClientRender,
36} from './ReactFiberFlags';
37import {
38  supportsPersistence,
39  getOffscreenContainerProps,
40} from './ReactFiberHostConfig';
41import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.old';
42import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
43import {
44  enableDebugTracing,
45  enableLazyContextPropagation,
46  enableUpdaterTracking,
47  enablePersistentOffscreenHostContainer,
48} from 'shared/ReactFeatureFlags';
49import {createCapturedValue} from './ReactCapturedValue';
50import {
51  enqueueCapturedUpdate,
52  createUpdate,
53  CaptureUpdate,
54  ForceUpdate,
55  enqueueUpdate,
56} from './ReactUpdateQueue.old';
57import {markFailedErrorBoundaryForHotReloading} from './ReactFiberHotReloading.old';
58import {
59  suspenseStackCursor,
60  InvisibleParentSuspenseContext,
61  hasSuspenseContext,
62} from './ReactFiberSuspenseContext.old';
63import {
64  renderDidError,
65  renderDidSuspendDelayIfPossible,
66  onUncaughtError,
67  markLegacyErrorBoundaryAsFailed,
68  isAlreadyFailedLegacyErrorBoundary,
69  pingSuspendedRoot,
70  restorePendingUpdaters,
71} from './ReactFiberWorkLoop.old';
72import {propagateParentContextChangesToDeferredTree} from './ReactFiberNewContext.old';
73import {logCapturedError} from './ReactFiberErrorLogger';
74import {logComponentSuspended} from './DebugTracing';
75import {isDevToolsPresent} from './ReactFiberDevToolsHook.old';
76import {
77  SyncLane,
78  NoTimestamp,
79  includesSomeLane,
80  mergeLanes,
81  pickArbitraryLane,
82  includesSyncLane,
83} from './ReactFiberLane.old';
84import {
85  getIsHydrating,
86  markDidSuspendWhileHydratingDEV,
87  queueHydrationError,
88} from './ReactFiberHydrationContext.old';
89
90const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
91
92function createRootErrorUpdate(
93  fiber: Fiber,
94  errorInfo: CapturedValue<mixed>,
95  lane: Lane,
96): Update<mixed> {
97  const update = createUpdate(NoTimestamp, lane);
98  // Unmount the root by rendering null.
99  update.tag = CaptureUpdate;
100  // Caution: React DevTools currently depends on this property
101  // being called "element".
102  update.payload = {element: null};
103  const error = errorInfo.value;
104  update.callback = () => {
105    onUncaughtError(error);
106    logCapturedError(fiber, errorInfo);
107  };
108  return update;
109}
110
111function createClassErrorUpdate(
112  fiber: Fiber,
113  errorInfo: CapturedValue<mixed>,
114  lane: Lane,
115): Update<mixed> {
116  const update = createUpdate(NoTimestamp, lane);
117  update.tag = CaptureUpdate;
118  const getDerivedStateFromError = fiber.type.getDerivedStateFromError;
119  if (typeof getDerivedStateFromError === 'function') {
120    const error = errorInfo.value;
121    update.payload = () => {
122      return getDerivedStateFromError(error);
123    };
124    update.callback = () => {
125      if (__DEV__) {
126        markFailedErrorBoundaryForHotReloading(fiber);
127      }
128      logCapturedError(fiber, errorInfo);
129    };
130  }
131
132  const inst = fiber.stateNode;
133  if (inst !== null && typeof inst.componentDidCatch === 'function') {
134    update.callback = function callback() {
135      if (__DEV__) {
136        markFailedErrorBoundaryForHotReloading(fiber);
137      }
138      logCapturedError(fiber, errorInfo);
139      if (typeof getDerivedStateFromError !== 'function') {
140        // To preserve the preexisting retry behavior of error boundaries,
141        // we keep track of which ones already failed during this batch.
142        // This gets reset before we yield back to the browser.
143        // TODO: Warn in strict mode if getDerivedStateFromError is
144        // not defined.
145        markLegacyErrorBoundaryAsFailed(this);
146      }
147      const error = errorInfo.value;
148      const stack = errorInfo.stack;
149      this.componentDidCatch(error, {
150        componentStack: stack !== null ? stack : '',
151      });
152      if (__DEV__) {
153        if (typeof getDerivedStateFromError !== 'function') {
154          // If componentDidCatch is the only error boundary method defined,
155          // then it needs to call setState to recover from errors.
156          // If no state update is scheduled then the boundary will swallow the error.
157          if (!includesSomeLane(fiber.lanes, (SyncLane: Lane))) {
158            console.error(
159              '%s: Error boundaries should implement getDerivedStateFromError(). ' +
160                'In that method, return a state update to display an error message or fallback UI.',
161              getComponentNameFromFiber(fiber) || 'Unknown',
162            );
163          }
164        }
165      }
166    };
167  }
168  return update;
169}
170
171function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: Lanes) {
172  // Attach a ping listener
173  //
174  // The data might resolve before we have a chance to commit the fallback. Or,
175  // in the case of a refresh, we'll never commit a fallback. So we need to
176  // attach a listener now. When it resolves ("pings"), we can decide whether to
177  // try rendering the tree again.
178  //
179  // Only attach a listener if one does not already exist for the lanes
180  // we're currently rendering (which acts like a "thread ID" here).
181  //
182  // We only need to do this in concurrent mode. Legacy Suspense always
183  // commits fallbacks synchronously, so there are no pings.
184  let pingCache = root.pingCache;
185  let threadIDs;
186  if (pingCache === null) {
187    pingCache = root.pingCache = new PossiblyWeakMap();
188    threadIDs = new Set();
189    pingCache.set(wakeable, threadIDs);
190  } else {
191    threadIDs = pingCache.get(wakeable);
192    if (threadIDs === undefined) {
193      threadIDs = new Set();
194      pingCache.set(wakeable, threadIDs);
195    }
196  }
197  if (!threadIDs.has(lanes)) {
198    // Memoize using the thread ID to prevent redundant listeners.
199    threadIDs.add(lanes);
200    const ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
201    if (enableUpdaterTracking) {
202      if (isDevToolsPresent) {
203        // If we have pending work still, restore the original updaters
204        restorePendingUpdaters(root, lanes);
205      }
206    }
207    wakeable.then(ping, ping);
208  }
209}
210
211function attachRetryListener(
212  suspenseBoundary: Fiber,
213  root: FiberRoot,
214  wakeable: Wakeable,
215  lanes: Lanes,
216) {
217  // Retry listener
218  //
219  // If the fallback does commit, we need to attach a different type of
220  // listener. This one schedules an update on the Suspense boundary to turn
221  // the fallback state off.
222  //
223  // Stash the wakeable on the boundary fiber so we can access it in the
224  // commit phase.
225  //
226  // When the wakeable resolves, we'll attempt to render the boundary
227  // again ("retry").
228  const wakeables: Set<Wakeable> | null = (suspenseBoundary.updateQueue: any);
229  if (wakeables === null) {
230    const updateQueue = (new Set(): any);
231    updateQueue.add(wakeable);
232    suspenseBoundary.updateQueue = updateQueue;
233  } else {
234    wakeables.add(wakeable);
235  }
236}
237
238function resetSuspendedComponent(sourceFiber: Fiber, rootRenderLanes: Lanes) {
239  if (enableLazyContextPropagation) {
240    const currentSourceFiber = sourceFiber.alternate;
241    if (currentSourceFiber !== null) {
242      // Since we never visited the children of the suspended component, we
243      // need to propagate the context change now, to ensure that we visit
244      // them during the retry.
245      //
246      // We don't have to do this for errors because we retry errors without
247      // committing in between. So this is specific to Suspense.
248      propagateParentContextChangesToDeferredTree(
249        currentSourceFiber,
250        sourceFiber,
251        rootRenderLanes,
252      );
253    }
254  }
255
256  // Reset the memoizedState to what it was before we attempted to render it.
257  // A legacy mode Suspense quirk, only relevant to hook components.
258  const tag = sourceFiber.tag;
259  if (
260    (sourceFiber.mode & ConcurrentMode) === NoMode &&
261    (tag === FunctionComponent ||
262      tag === ForwardRef ||
263      tag === SimpleMemoComponent)
264  ) {
265    const currentSource = sourceFiber.alternate;
266    if (currentSource) {
267      sourceFiber.updateQueue = currentSource.updateQueue;
268      sourceFiber.memoizedState = currentSource.memoizedState;
269      sourceFiber.lanes = currentSource.lanes;
270    } else {
271      sourceFiber.updateQueue = null;
272      sourceFiber.memoizedState = null;
273    }
274  }
275}
276
277function getNearestSuspenseBoundaryToCapture(returnFiber: Fiber) {
278  let node = returnFiber;
279  const hasInvisibleParentBoundary = hasSuspenseContext(
280    suspenseStackCursor.current,
281    (InvisibleParentSuspenseContext: SuspenseContext),
282  );
283  do {
284    if (
285      node.tag === SuspenseComponent &&
286      shouldCaptureSuspense(node, hasInvisibleParentBoundary)
287    ) {
288      return node;
289    }
290    // This boundary already captured during this render. Continue to the next
291    // boundary.
292    node = node.return;
293  } while (node !== null);
294  return null;
295}
296
297function markSuspenseBoundaryShouldCapture(
298  suspenseBoundary: Fiber,
299  returnFiber: Fiber,
300  sourceFiber: Fiber,
301  root: FiberRoot,
302  rootRenderLanes: Lanes,
303): Fiber | null {
304  // This marks a Suspense boundary so that when we're unwinding the stack,
305  // it captures the suspended "exception" and does a second (fallback) pass.
306  if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
307    // Legacy Mode Suspense
308    //
309    // If the boundary is in legacy mode, we should *not*
310    // suspend the commit. Pretend as if the suspended component rendered
311    // null and keep rendering. When the Suspense boundary completes,
312    // we'll do a second pass to render the fallback.
313    if (suspenseBoundary === returnFiber) {
314      // Special case where we suspended while reconciling the children of
315      // a Suspense boundary's inner Offscreen wrapper fiber. This happens
316      // when a React.lazy component is a direct child of a
317      // Suspense boundary.
318      //
319      // Suspense boundaries are implemented as multiple fibers, but they
320      // are a single conceptual unit. The legacy mode behavior where we
321      // pretend the suspended fiber committed as `null` won't work,
322      // because in this case the "suspended" fiber is the inner
323      // Offscreen wrapper.
324      //
325      // Because the contents of the boundary haven't started rendering
326      // yet (i.e. nothing in the tree has partially rendered) we can
327      // switch to the regular, concurrent mode behavior: mark the
328      // boundary with ShouldCapture and enter the unwind phase.
329      suspenseBoundary.flags |= ShouldCapture;
330    } else {
331      suspenseBoundary.flags |= DidCapture;
332      sourceFiber.flags |= ForceUpdateForLegacySuspense;
333
334      // We're going to commit this fiber even though it didn't complete.
335      // But we shouldn't call any lifecycle methods or callbacks. Remove
336      // all lifecycle effect tags.
337      sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
338
339      if (supportsPersistence && enablePersistentOffscreenHostContainer) {
340        // Another legacy Suspense quirk. In persistent mode, if this is the
341        // initial mount, override the props of the host container to hide
342        // its contents.
343        const currentSuspenseBoundary = suspenseBoundary.alternate;
344        if (currentSuspenseBoundary === null) {
345          const offscreenFiber: Fiber = (suspenseBoundary.child: any);
346          const offscreenContainer = offscreenFiber.child;
347          if (offscreenContainer !== null) {
348            const children = offscreenContainer.memoizedProps.children;
349            const containerProps = getOffscreenContainerProps(
350              'hidden',
351              children,
352            );
353            offscreenContainer.pendingProps = containerProps;
354            offscreenContainer.memoizedProps = containerProps;
355          }
356        }
357      }
358
359      if (sourceFiber.tag === ClassComponent) {
360        const currentSourceFiber = sourceFiber.alternate;
361        if (currentSourceFiber === null) {
362          // This is a new mount. Change the tag so it's not mistaken for a
363          // completed class component. For example, we should not call
364          // componentWillUnmount if it is deleted.
365          sourceFiber.tag = IncompleteClassComponent;
366        } else {
367          // When we try rendering again, we should not reuse the current fiber,
368          // since it's known to be in an inconsistent state. Use a force update to
369          // prevent a bail out.
370          const update = createUpdate(NoTimestamp, SyncLane);
371          update.tag = ForceUpdate;
372          enqueueUpdate(sourceFiber, update, SyncLane);
373        }
374      }
375
376      // The source fiber did not complete. Mark it with Sync priority to
377      // indicate that it still has pending work.
378      sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
379    }
380    return suspenseBoundary;
381  }
382  // Confirmed that the boundary is in a concurrent mode tree. Continue
383  // with the normal suspend path.
384  //
385  // After this we'll use a set of heuristics to determine whether this
386  // render pass will run to completion or restart or "suspend" the commit.
387  // The actual logic for this is spread out in different places.
388  //
389  // This first principle is that if we're going to suspend when we complete
390  // a root, then we should also restart if we get an update or ping that
391  // might unsuspend it, and vice versa. The only reason to suspend is
392  // because you think you might want to restart before committing. However,
393  // it doesn't make sense to restart only while in the period we're suspended.
394  //
395  // Restarting too aggressively is also not good because it starves out any
396  // intermediate loading state. So we use heuristics to determine when.
397
398  // Suspense Heuristics
399  //
400  // If nothing threw a Promise or all the same fallbacks are already showing,
401  // then don't suspend/restart.
402  //
403  // If this is an initial render of a new tree of Suspense boundaries and
404  // those trigger a fallback, then don't suspend/restart. We want to ensure
405  // that we can show the initial loading state as quickly as possible.
406  //
407  // If we hit a "Delayed" case, such as when we'd switch from content back into
408  // a fallback, then we should always suspend/restart. Transitions apply
409  // to this case. If none is defined, JND is used instead.
410  //
411  // If we're already showing a fallback and it gets "retried", allowing us to show
412  // another level, but there's still an inner boundary that would show a fallback,
413  // then we suspend/restart for 500ms since the last time we showed a fallback
414  // anywhere in the tree. This effectively throttles progressive loading into a
415  // consistent train of commits. This also gives us an opportunity to restart to
416  // get to the completed state slightly earlier.
417  //
418  // If there's ambiguity due to batching it's resolved in preference of:
419  // 1) "delayed", 2) "initial render", 3) "retry".
420  //
421  // We want to ensure that a "busy" state doesn't get force committed. We want to
422  // ensure that new initial loading states can commit as soon as possible.
423  suspenseBoundary.flags |= ShouldCapture;
424  // TODO: I think we can remove this, since we now use `DidCapture` in
425  // the begin phase to prevent an early bailout.
426  suspenseBoundary.lanes = rootRenderLanes;
427  return suspenseBoundary;
428}
429
430function throwException(
431  root: FiberRoot,
432  returnFiber: Fiber,
433  sourceFiber: Fiber,
434  value: mixed,
435  rootRenderLanes: Lanes,
436) {
437  // The source fiber did not complete.
438  sourceFiber.flags |= Incomplete;
439
440  if (enableUpdaterTracking) {
441    if (isDevToolsPresent) {
442      // If we have pending work still, restore the original updaters
443      restorePendingUpdaters(root, rootRenderLanes);
444    }
445  }
446
447  if (
448    value !== null &&
449    typeof value === 'object' &&
450    typeof value.then === 'function'
451  ) {
452    // This is a wakeable. The component suspended.
453    const wakeable: Wakeable = (value: any);
454    resetSuspendedComponent(sourceFiber, rootRenderLanes);
455
456    if (__DEV__) {
457      if (enableDebugTracing) {
458        if (sourceFiber.mode & DebugTracingMode) {
459          const name = getComponentNameFromFiber(sourceFiber) || 'Unknown';
460          logComponentSuspended(name, wakeable);
461        }
462      }
463    }
464
465    // Schedule the nearest Suspense to re-render the timed out view.
466    const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
467    if (suspenseBoundary !== null) {
468      suspenseBoundary.flags &= ~ForceClientRender;
469      markSuspenseBoundaryShouldCapture(
470        suspenseBoundary,
471        returnFiber,
472        sourceFiber,
473        root,
474        rootRenderLanes,
475      );
476      // We only attach ping listeners in concurrent mode. Legacy Suspense always
477      // commits fallbacks synchronously, so there are no pings.
478      if (suspenseBoundary.mode & ConcurrentMode) {
479        attachPingListener(root, wakeable, rootRenderLanes);
480      }
481      attachRetryListener(suspenseBoundary, root, wakeable, rootRenderLanes);
482      return;
483    } else {
484      // No boundary was found. Unless this is a sync update, this is OK.
485      // We can suspend and wait for more data to arrive.
486
487      if (!includesSyncLane(rootRenderLanes)) {
488        // This is not a sync update. Suspend. Since we're not activating a
489        // Suspense boundary, this will unwind all the way to the root without
490        // performing a second pass to render a fallback. (This is arguably how
491        // refresh transitions should work, too, since we're not going to commit
492        // the fallbacks anyway.)
493        //
494        // This case also applies to initial hydration.
495        attachPingListener(root, wakeable, rootRenderLanes);
496        renderDidSuspendDelayIfPossible();
497        return;
498      }
499
500      // This is a sync/discrete update. We treat this case like an error
501      // because discrete renders are expected to produce a complete tree
502      // synchronously to maintain consistency with external state.
503      const uncaughtSuspenseError = new Error(
504        'A component suspended while responding to synchronous input. This ' +
505          'will cause the UI to be replaced with a loading indicator. To ' +
506          'fix, updates that suspend should be wrapped ' +
507          'with startTransition.',
508      );
509
510      // If we're outside a transition, fall through to the regular error path.
511      // The error will be caught by the nearest suspense boundary.
512      value = uncaughtSuspenseError;
513    }
514  } else {
515    // This is a regular error, not a Suspense wakeable.
516    if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
517      markDidSuspendWhileHydratingDEV();
518
519      const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
520      // If the error was thrown during hydration, we may be able to recover by
521      // discarding the dehydrated content and switching to a client render.
522      // Instead of surfacing the error, find the nearest Suspense boundary
523      // and render it again without hydration.
524      if (suspenseBoundary !== null) {
525        if ((suspenseBoundary.flags & ShouldCapture) === NoFlags) {
526          // Set a flag to indicate that we should try rendering the normal
527          // children again, not the fallback.
528          suspenseBoundary.flags |= ForceClientRender;
529        }
530        markSuspenseBoundaryShouldCapture(
531          suspenseBoundary,
532          returnFiber,
533          sourceFiber,
534          root,
535          rootRenderLanes,
536        );
537
538        // Even though the user may not be affected by this error, we should
539        // still log it so it can be fixed.
540        queueHydrationError(value);
541        return;
542      }
543    } else {
544      // Otherwise, fall through to the error path.
545    }
546  }
547
548  // We didn't find a boundary that could handle this type of exception. Start
549  // over and traverse parent path again, this time treating the exception
550  // as an error.
551  renderDidError(value);
552
553  value = createCapturedValue(value, sourceFiber);
554  let workInProgress = returnFiber;
555  do {
556    switch (workInProgress.tag) {
557      case HostRoot: {
558        const errorInfo = value;
559        workInProgress.flags |= ShouldCapture;
560        const lane = pickArbitraryLane(rootRenderLanes);
561        workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
562        const update = createRootErrorUpdate(workInProgress, errorInfo, lane);
563        enqueueCapturedUpdate(workInProgress, update);
564        return;
565      }
566      case ClassComponent:
567        // Capture and retry
568        const errorInfo = value;
569        const ctor = workInProgress.type;
570        const instance = workInProgress.stateNode;
571        if (
572          (workInProgress.flags & DidCapture) === NoFlags &&
573          (typeof ctor.getDerivedStateFromError === 'function' ||
574            (instance !== null &&
575              typeof instance.componentDidCatch === 'function' &&
576              !isAlreadyFailedLegacyErrorBoundary(instance)))
577        ) {
578          workInProgress.flags |= ShouldCapture;
579          const lane = pickArbitraryLane(rootRenderLanes);
580          workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
581          // Schedule the error boundary to re-render using updated state
582          const update = createClassErrorUpdate(
583            workInProgress,
584            errorInfo,
585            lane,
586          );
587          enqueueCapturedUpdate(workInProgress, update);
588          return;
589        }
590        break;
591      default:
592        break;
593    }
594    workInProgress = workInProgress.return;
595  } while (workInProgress !== null);
596}
597
598export {throwException, createRootErrorUpdate, createClassErrorUpdate};
599
Full Screen

ReactFiberThrow.new.js

Source: ReactFiberThrow.new.js Github

copy
1/**
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10import type {Fiber} from './ReactInternalTypes';
11import type {FiberRoot} from './ReactInternalTypes';
12import type {Lane, Lanes} from './ReactFiberLane.new';
13import type {CapturedValue} from './ReactCapturedValue';
14import type {Update} from './ReactUpdateQueue.new';
15import type {Wakeable} from 'shared/ReactTypes';
16import type {SuspenseContext} from './ReactFiberSuspenseContext.new';
17
18import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
19import {
20  ClassComponent,
21  HostRoot,
22  SuspenseComponent,
23  IncompleteClassComponent,
24  FunctionComponent,
25  ForwardRef,
26  SimpleMemoComponent,
27} from './ReactWorkTags';
28import {
29  DidCapture,
30  Incomplete,
31  NoFlags,
32  ShouldCapture,
33  LifecycleEffectMask,
34  ForceUpdateForLegacySuspense,
35  ForceClientRender,
36} from './ReactFiberFlags';
37import {
38  supportsPersistence,
39  getOffscreenContainerProps,
40} from './ReactFiberHostConfig';
41import {shouldCaptureSuspense} from './ReactFiberSuspenseComponent.new';
42import {NoMode, ConcurrentMode, DebugTracingMode} from './ReactTypeOfMode';
43import {
44  enableDebugTracing,
45  enableLazyContextPropagation,
46  enableUpdaterTracking,
47  enablePersistentOffscreenHostContainer,
48} from 'shared/ReactFeatureFlags';
49import {createCapturedValue} from './ReactCapturedValue';
50import {
51  enqueueCapturedUpdate,
52  createUpdate,
53  CaptureUpdate,
54  ForceUpdate,
55  enqueueUpdate,
56} from './ReactUpdateQueue.new';
57import {markFailedErrorBoundaryForHotReloading} from './ReactFiberHotReloading.new';
58import {
59  suspenseStackCursor,
60  InvisibleParentSuspenseContext,
61  hasSuspenseContext,
62} from './ReactFiberSuspenseContext.new';
63import {
64  renderDidError,
65  renderDidSuspendDelayIfPossible,
66  onUncaughtError,
67  markLegacyErrorBoundaryAsFailed,
68  isAlreadyFailedLegacyErrorBoundary,
69  pingSuspendedRoot,
70  restorePendingUpdaters,
71} from './ReactFiberWorkLoop.new';
72import {propagateParentContextChangesToDeferredTree} from './ReactFiberNewContext.new';
73import {logCapturedError} from './ReactFiberErrorLogger';
74import {logComponentSuspended} from './DebugTracing';
75import {isDevToolsPresent} from './ReactFiberDevToolsHook.new';
76import {
77  SyncLane,
78  NoTimestamp,
79  includesSomeLane,
80  mergeLanes,
81  pickArbitraryLane,
82  includesSyncLane,
83} from './ReactFiberLane.new';
84import {
85  getIsHydrating,
86  markDidSuspendWhileHydratingDEV,
87  queueHydrationError,
88} from './ReactFiberHydrationContext.new';
89
90const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
91
92function createRootErrorUpdate(
93  fiber: Fiber,
94  errorInfo: CapturedValue<mixed>,
95  lane: Lane,
96): Update<mixed> {
97  const update = createUpdate(NoTimestamp, lane);
98  // Unmount the root by rendering null.
99  update.tag = CaptureUpdate;
100  // Caution: React DevTools currently depends on this property
101  // being called "element".
102  update.payload = {element: null};
103  const error = errorInfo.value;
104  update.callback = () => {
105    onUncaughtError(error);
106    logCapturedError(fiber, errorInfo);
107  };
108  return update;
109}
110
111function createClassErrorUpdate(
112  fiber: Fiber,
113  errorInfo: CapturedValue<mixed>,
114  lane: Lane,
115): Update<mixed> {
116  const update = createUpdate(NoTimestamp, lane);
117  update.tag = CaptureUpdate;
118  const getDerivedStateFromError = fiber.type.getDerivedStateFromError;
119  if (typeof getDerivedStateFromError === 'function') {
120    const error = errorInfo.value;
121    update.payload = () => {
122      return getDerivedStateFromError(error);
123    };
124    update.callback = () => {
125      if (__DEV__) {
126        markFailedErrorBoundaryForHotReloading(fiber);
127      }
128      logCapturedError(fiber, errorInfo);
129    };
130  }
131
132  const inst = fiber.stateNode;
133  if (inst !== null && typeof inst.componentDidCatch === 'function') {
134    update.callback = function callback() {
135      if (__DEV__) {
136        markFailedErrorBoundaryForHotReloading(fiber);
137      }
138      logCapturedError(fiber, errorInfo);
139      if (typeof getDerivedStateFromError !== 'function') {
140        // To preserve the preexisting retry behavior of error boundaries,
141        // we keep track of which ones already failed during this batch.
142        // This gets reset before we yield back to the browser.
143        // TODO: Warn in strict mode if getDerivedStateFromError is
144        // not defined.
145        markLegacyErrorBoundaryAsFailed(this);
146      }
147      const error = errorInfo.value;
148      const stack = errorInfo.stack;
149      this.componentDidCatch(error, {
150        componentStack: stack !== null ? stack : '',
151      });
152      if (__DEV__) {
153        if (typeof getDerivedStateFromError !== 'function') {
154          // If componentDidCatch is the only error boundary method defined,
155          // then it needs to call setState to recover from errors.
156          // If no state update is scheduled then the boundary will swallow the error.
157          if (!includesSomeLane(fiber.lanes, (SyncLane: Lane))) {
158            console.error(
159              '%s: Error boundaries should implement getDerivedStateFromError(). ' +
160                'In that method, return a state update to display an error message or fallback UI.',
161              getComponentNameFromFiber(fiber) || 'Unknown',
162            );
163          }
164        }
165      }
166    };
167  }
168  return update;
169}
170
171function attachPingListener(root: FiberRoot, wakeable: Wakeable, lanes: Lanes) {
172  // Attach a ping listener
173  //
174  // The data might resolve before we have a chance to commit the fallback. Or,
175  // in the case of a refresh, we'll never commit a fallback. So we need to
176  // attach a listener now. When it resolves ("pings"), we can decide whether to
177  // try rendering the tree again.
178  //
179  // Only attach a listener if one does not already exist for the lanes
180  // we're currently rendering (which acts like a "thread ID" here).
181  //
182  // We only need to do this in concurrent mode. Legacy Suspense always
183  // commits fallbacks synchronously, so there are no pings.
184  let pingCache = root.pingCache;
185  let threadIDs;
186  if (pingCache === null) {
187    pingCache = root.pingCache = new PossiblyWeakMap();
188    threadIDs = new Set();
189    pingCache.set(wakeable, threadIDs);
190  } else {
191    threadIDs = pingCache.get(wakeable);
192    if (threadIDs === undefined) {
193      threadIDs = new Set();
194      pingCache.set(wakeable, threadIDs);
195    }
196  }
197  if (!threadIDs.has(lanes)) {
198    // Memoize using the thread ID to prevent redundant listeners.
199    threadIDs.add(lanes);
200    const ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
201    if (enableUpdaterTracking) {
202      if (isDevToolsPresent) {
203        // If we have pending work still, restore the original updaters
204        restorePendingUpdaters(root, lanes);
205      }
206    }
207    wakeable.then(ping, ping);
208  }
209}
210
211function attachRetryListener(
212  suspenseBoundary: Fiber,
213  root: FiberRoot,
214  wakeable: Wakeable,
215  lanes: Lanes,
216) {
217  // Retry listener
218  //
219  // If the fallback does commit, we need to attach a different type of
220  // listener. This one schedules an update on the Suspense boundary to turn
221  // the fallback state off.
222  //
223  // Stash the wakeable on the boundary fiber so we can access it in the
224  // commit phase.
225  //
226  // When the wakeable resolves, we'll attempt to render the boundary
227  // again ("retry").
228  const wakeables: Set<Wakeable> | null = (suspenseBoundary.updateQueue: any);
229  if (wakeables === null) {
230    const updateQueue = (new Set(): any);
231    updateQueue.add(wakeable);
232    suspenseBoundary.updateQueue = updateQueue;
233  } else {
234    wakeables.add(wakeable);
235  }
236}
237
238function resetSuspendedComponent(sourceFiber: Fiber, rootRenderLanes: Lanes) {
239  if (enableLazyContextPropagation) {
240    const currentSourceFiber = sourceFiber.alternate;
241    if (currentSourceFiber !== null) {
242      // Since we never visited the children of the suspended component, we
243      // need to propagate the context change now, to ensure that we visit
244      // them during the retry.
245      //
246      // We don't have to do this for errors because we retry errors without
247      // committing in between. So this is specific to Suspense.
248      propagateParentContextChangesToDeferredTree(
249        currentSourceFiber,
250        sourceFiber,
251        rootRenderLanes,
252      );
253    }
254  }
255
256  // Reset the memoizedState to what it was before we attempted to render it.
257  // A legacy mode Suspense quirk, only relevant to hook components.
258  const tag = sourceFiber.tag;
259  if (
260    (sourceFiber.mode & ConcurrentMode) === NoMode &&
261    (tag === FunctionComponent ||
262      tag === ForwardRef ||
263      tag === SimpleMemoComponent)
264  ) {
265    const currentSource = sourceFiber.alternate;
266    if (currentSource) {
267      sourceFiber.updateQueue = currentSource.updateQueue;
268      sourceFiber.memoizedState = currentSource.memoizedState;
269      sourceFiber.lanes = currentSource.lanes;
270    } else {
271      sourceFiber.updateQueue = null;
272      sourceFiber.memoizedState = null;
273    }
274  }
275}
276
277function getNearestSuspenseBoundaryToCapture(returnFiber: Fiber) {
278  let node = returnFiber;
279  const hasInvisibleParentBoundary = hasSuspenseContext(
280    suspenseStackCursor.current,
281    (InvisibleParentSuspenseContext: SuspenseContext),
282  );
283  do {
284    if (
285      node.tag === SuspenseComponent &&
286      shouldCaptureSuspense(node, hasInvisibleParentBoundary)
287    ) {
288      return node;
289    }
290    // This boundary already captured during this render. Continue to the next
291    // boundary.
292    node = node.return;
293  } while (node !== null);
294  return null;
295}
296
297function markSuspenseBoundaryShouldCapture(
298  suspenseBoundary: Fiber,
299  returnFiber: Fiber,
300  sourceFiber: Fiber,
301  root: FiberRoot,
302  rootRenderLanes: Lanes,
303): Fiber | null {
304  // This marks a Suspense boundary so that when we're unwinding the stack,
305  // it captures the suspended "exception" and does a second (fallback) pass.
306  if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
307    // Legacy Mode Suspense
308    //
309    // If the boundary is in legacy mode, we should *not*
310    // suspend the commit. Pretend as if the suspended component rendered
311    // null and keep rendering. When the Suspense boundary completes,
312    // we'll do a second pass to render the fallback.
313    if (suspenseBoundary === returnFiber) {
314      // Special case where we suspended while reconciling the children of
315      // a Suspense boundary's inner Offscreen wrapper fiber. This happens
316      // when a React.lazy component is a direct child of a
317      // Suspense boundary.
318      //
319      // Suspense boundaries are implemented as multiple fibers, but they
320      // are a single conceptual unit. The legacy mode behavior where we
321      // pretend the suspended fiber committed as `null` won't work,
322      // because in this case the "suspended" fiber is the inner
323      // Offscreen wrapper.
324      //
325      // Because the contents of the boundary haven't started rendering
326      // yet (i.e. nothing in the tree has partially rendered) we can
327      // switch to the regular, concurrent mode behavior: mark the
328      // boundary with ShouldCapture and enter the unwind phase.
329      suspenseBoundary.flags |= ShouldCapture;
330    } else {
331      suspenseBoundary.flags |= DidCapture;
332      sourceFiber.flags |= ForceUpdateForLegacySuspense;
333
334      // We're going to commit this fiber even though it didn't complete.
335      // But we shouldn't call any lifecycle methods or callbacks. Remove
336      // all lifecycle effect tags.
337      sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
338
339      if (supportsPersistence && enablePersistentOffscreenHostContainer) {
340        // Another legacy Suspense quirk. In persistent mode, if this is the
341        // initial mount, override the props of the host container to hide
342        // its contents.
343        const currentSuspenseBoundary = suspenseBoundary.alternate;
344        if (currentSuspenseBoundary === null) {
345          const offscreenFiber: Fiber = (suspenseBoundary.child: any);
346          const offscreenContainer = offscreenFiber.child;
347          if (offscreenContainer !== null) {
348            const children = offscreenContainer.memoizedProps.children;
349            const containerProps = getOffscreenContainerProps(
350              'hidden',
351              children,
352            );
353            offscreenContainer.pendingProps = containerProps;
354            offscreenContainer.memoizedProps = containerProps;
355          }
356        }
357      }
358
359      if (sourceFiber.tag === ClassComponent) {
360        const currentSourceFiber = sourceFiber.alternate;
361        if (currentSourceFiber === null) {
362          // This is a new mount. Change the tag so it's not mistaken for a
363          // completed class component. For example, we should not call
364          // componentWillUnmount if it is deleted.
365          sourceFiber.tag = IncompleteClassComponent;
366        } else {
367          // When we try rendering again, we should not reuse the current fiber,
368          // since it's known to be in an inconsistent state. Use a force update to
369          // prevent a bail out.
370          const update = createUpdate(NoTimestamp, SyncLane);
371          update.tag = ForceUpdate;
372          enqueueUpdate(sourceFiber, update, SyncLane);
373        }
374      }
375
376      // The source fiber did not complete. Mark it with Sync priority to
377      // indicate that it still has pending work.
378      sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
379    }
380    return suspenseBoundary;
381  }
382  // Confirmed that the boundary is in a concurrent mode tree. Continue
383  // with the normal suspend path.
384  //
385  // After this we'll use a set of heuristics to determine whether this
386  // render pass will run to completion or restart or "suspend" the commit.
387  // The actual logic for this is spread out in different places.
388  //
389  // This first principle is that if we're going to suspend when we complete
390  // a root, then we should also restart if we get an update or ping that
391  // might unsuspend it, and vice versa. The only reason to suspend is
392  // because you think you might want to restart before committing. However,
393  // it doesn't make sense to restart only while in the period we're suspended.
394  //
395  // Restarting too aggressively is also not good because it starves out any
396  // intermediate loading state. So we use heuristics to determine when.
397
398  // Suspense Heuristics
399  //
400  // If nothing threw a Promise or all the same fallbacks are already showing,
401  // then don't suspend/restart.
402  //
403  // If this is an initial render of a new tree of Suspense boundaries and
404  // those trigger a fallback, then don't suspend/restart. We want to ensure
405  // that we can show the initial loading state as quickly as possible.
406  //
407  // If we hit a "Delayed" case, such as when we'd switch from content back into
408  // a fallback, then we should always suspend/restart. Transitions apply
409  // to this case. If none is defined, JND is used instead.
410  //
411  // If we're already showing a fallback and it gets "retried", allowing us to show
412  // another level, but there's still an inner boundary that would show a fallback,
413  // then we suspend/restart for 500ms since the last time we showed a fallback
414  // anywhere in the tree. This effectively throttles progressive loading into a
415  // consistent train of commits. This also gives us an opportunity to restart to
416  // get to the completed state slightly earlier.
417  //
418  // If there's ambiguity due to batching it's resolved in preference of:
419  // 1) "delayed", 2) "initial render", 3) "retry".
420  //
421  // We want to ensure that a "busy" state doesn't get force committed. We want to
422  // ensure that new initial loading states can commit as soon as possible.
423  suspenseBoundary.flags |= ShouldCapture;
424  // TODO: I think we can remove this, since we now use `DidCapture` in
425  // the begin phase to prevent an early bailout.
426  suspenseBoundary.lanes = rootRenderLanes;
427  return suspenseBoundary;
428}
429
430function throwException(
431  root: FiberRoot,
432  returnFiber: Fiber,
433  sourceFiber: Fiber,
434  value: mixed,
435  rootRenderLanes: Lanes,
436) {
437  // The source fiber did not complete.
438  sourceFiber.flags |= Incomplete;
439
440  if (enableUpdaterTracking) {
441    if (isDevToolsPresent) {
442      // If we have pending work still, restore the original updaters
443      restorePendingUpdaters(root, rootRenderLanes);
444    }
445  }
446
447  if (
448    value !== null &&
449    typeof value === 'object' &&
450    typeof value.then === 'function'
451  ) {
452    // This is a wakeable. The component suspended.
453    const wakeable: Wakeable = (value: any);
454    resetSuspendedComponent(sourceFiber, rootRenderLanes);
455
456    if (__DEV__) {
457      if (enableDebugTracing) {
458        if (sourceFiber.mode & DebugTracingMode) {
459          const name = getComponentNameFromFiber(sourceFiber) || 'Unknown';
460          logComponentSuspended(name, wakeable);
461        }
462      }
463    }
464
465    // Schedule the nearest Suspense to re-render the timed out view.
466    const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
467    if (suspenseBoundary !== null) {
468      suspenseBoundary.flags &= ~ForceClientRender;
469      markSuspenseBoundaryShouldCapture(
470        suspenseBoundary,
471        returnFiber,
472        sourceFiber,
473        root,
474        rootRenderLanes,
475      );
476      // We only attach ping listeners in concurrent mode. Legacy Suspense always
477      // commits fallbacks synchronously, so there are no pings.
478      if (suspenseBoundary.mode & ConcurrentMode) {
479        attachPingListener(root, wakeable, rootRenderLanes);
480      }
481      attachRetryListener(suspenseBoundary, root, wakeable, rootRenderLanes);
482      return;
483    } else {
484      // No boundary was found. Unless this is a sync update, this is OK.
485      // We can suspend and wait for more data to arrive.
486
487      if (!includesSyncLane(rootRenderLanes)) {
488        // This is not a sync update. Suspend. Since we're not activating a
489        // Suspense boundary, this will unwind all the way to the root without
490        // performing a second pass to render a fallback. (This is arguably how
491        // refresh transitions should work, too, since we're not going to commit
492        // the fallbacks anyway.)
493        //
494        // This case also applies to initial hydration.
495        attachPingListener(root, wakeable, rootRenderLanes);
496        renderDidSuspendDelayIfPossible();
497        return;
498      }
499
500      // This is a sync/discrete update. We treat this case like an error
501      // because discrete renders are expected to produce a complete tree
502      // synchronously to maintain consistency with external state.
503      const uncaughtSuspenseError = new Error(
504        'A component suspended while responding to synchronous input. This ' +
505          'will cause the UI to be replaced with a loading indicator. To ' +
506          'fix, updates that suspend should be wrapped ' +
507          'with startTransition.',
508      );
509
510      // If we're outside a transition, fall through to the regular error path.
511      // The error will be caught by the nearest suspense boundary.
512      value = uncaughtSuspenseError;
513    }
514  } else {
515    // This is a regular error, not a Suspense wakeable.
516    if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
517      markDidSuspendWhileHydratingDEV();
518
519      const suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
520      // If the error was thrown during hydration, we may be able to recover by
521      // discarding the dehydrated content and switching to a client render.
522      // Instead of surfacing the error, find the nearest Suspense boundary
523      // and render it again without hydration.
524      if (suspenseBoundary !== null) {
525        if ((suspenseBoundary.flags & ShouldCapture) === NoFlags) {
526          // Set a flag to indicate that we should try rendering the normal
527          // children again, not the fallback.
528          suspenseBoundary.flags |= ForceClientRender;
529        }
530        markSuspenseBoundaryShouldCapture(
531          suspenseBoundary,
532          returnFiber,
533          sourceFiber,
534          root,
535          rootRenderLanes,
536        );
537
538        // Even though the user may not be affected by this error, we should
539        // still log it so it can be fixed.
540        queueHydrationError(value);
541        return;
542      }
543    } else {
544      // Otherwise, fall through to the error path.
545    }
546  }
547
548  // We didn't find a boundary that could handle this type of exception. Start
549  // over and traverse parent path again, this time treating the exception
550  // as an error.
551  renderDidError(value);
552
553  value = createCapturedValue(value, sourceFiber);
554  let workInProgress = returnFiber;
555  do {
556    switch (workInProgress.tag) {
557      case HostRoot: {
558        const errorInfo = value;
559        workInProgress.flags |= ShouldCapture;
560        const lane = pickArbitraryLane(rootRenderLanes);
561        workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
562        const update = createRootErrorUpdate(workInProgress, errorInfo, lane);
563        enqueueCapturedUpdate(workInProgress, update);
564        return;
565      }
566      case ClassComponent:
567        // Capture and retry
568        const errorInfo = value;
569        const ctor = workInProgress.type;
570        const instance = workInProgress.stateNode;
571        if (
572          (workInProgress.flags & DidCapture) === NoFlags &&
573          (typeof ctor.getDerivedStateFromError === 'function' ||
574            (instance !== null &&
575              typeof instance.componentDidCatch === 'function' &&
576              !isAlreadyFailedLegacyErrorBoundary(instance)))
577        ) {
578          workInProgress.flags |= ShouldCapture;
579          const lane = pickArbitraryLane(rootRenderLanes);
580          workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
581          // Schedule the error boundary to re-render using updated state
582          const update = createClassErrorUpdate(
583            workInProgress,
584            errorInfo,
585            lane,
586          );
587          enqueueCapturedUpdate(workInProgress, update);
588          return;
589        }
590        break;
591      default:
592        break;
593    }
594    workInProgress = workInProgress.return;
595  } while (workInProgress !== null);
596}
597
598export {throwException, createRootErrorUpdate, createClassErrorUpdate};
599
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Playwright Internal on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)