(current, workInProgress, renderExpirationTime)
| 17748 | } |
| 17749 | |
| 17750 | function updateSuspenseComponent(current, workInProgress, renderExpirationTime) { |
| 17751 | var mode = workInProgress.mode; |
| 17752 | var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. |
| 17753 | |
| 17754 | { |
| 17755 | if (shouldSuspend(workInProgress)) { |
| 17756 | workInProgress.effectTag |= DidCapture; |
| 17757 | } |
| 17758 | } |
| 17759 | |
| 17760 | var suspenseContext = suspenseStackCursor.current; |
| 17761 | var nextDidTimeout = false; |
| 17762 | var didSuspend = (workInProgress.effectTag & DidCapture) !== NoEffect; |
| 17763 | |
| 17764 | if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { |
| 17765 | // Something in this boundary's subtree already suspended. Switch to |
| 17766 | // rendering the fallback children. |
| 17767 | nextDidTimeout = true; |
| 17768 | workInProgress.effectTag &= ~DidCapture; |
| 17769 | } else { |
| 17770 | // Attempting the main content |
| 17771 | if (current === null || current.memoizedState !== null) { |
| 17772 | // This is a new mount or this boundary is already showing a fallback state. |
| 17773 | // Mark this subtree context as having at least one invisible parent that could |
| 17774 | // handle the fallback state. |
| 17775 | // Boundaries without fallbacks or should be avoided are not considered since |
| 17776 | // they cannot handle preferred fallback states. |
| 17777 | if (nextProps.fallback !== undefined && nextProps.unstable_avoidThisFallback !== true) { |
| 17778 | suspenseContext = addSubtreeSuspenseContext(suspenseContext, InvisibleParentSuspenseContext); |
| 17779 | } |
| 17780 | } |
| 17781 | } |
| 17782 | |
| 17783 | suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); |
| 17784 | pushSuspenseContext(workInProgress, suspenseContext); // This next part is a bit confusing. If the children timeout, we switch to |
| 17785 | // showing the fallback children in place of the "primary" children. |
| 17786 | // However, we don't want to delete the primary children because then their |
| 17787 | // state will be lost (both the React state and the host state, e.g. |
| 17788 | // uncontrolled form inputs). Instead we keep them mounted and hide them. |
| 17789 | // Both the fallback children AND the primary children are rendered at the |
| 17790 | // same time. Once the primary children are un-suspended, we can delete |
| 17791 | // the fallback children — don't need to preserve their state. |
| 17792 | // |
| 17793 | // The two sets of children are siblings in the host environment, but |
| 17794 | // semantically, for purposes of reconciliation, they are two separate sets. |
| 17795 | // So we store them using two fragment fibers. |
| 17796 | // |
| 17797 | // However, we want to avoid allocating extra fibers for every placeholder. |
| 17798 | // They're only necessary when the children time out, because that's the |
| 17799 | // only time when both sets are mounted. |
| 17800 | // |
| 17801 | // So, the extra fragment fibers are only used if the children time out. |
| 17802 | // Otherwise, we render the primary children directly. This requires some |
| 17803 | // custom reconciliation logic to preserve the state of the primary |
| 17804 | // children. It's essentially a very basic form of re-parenting. |
| 17805 | |
| 17806 | if (current === null) { |
| 17807 | // If we're currently hydrating, try to hydrate this boundary. |
no test coverage detected