mirror of
https://github.com/zebrajr/react.git
synced 2025-12-07 12:20:38 +01:00
This adds an experimental `unstable_postpone(reason)` API. Currently we don't have a way to model effectively an Infinite Promise. I.e. something that suspends but never resolves. The reason this is useful is because you might have something else that unblocks it later. E.g. by updating in place later, or by client rendering. On the client this works to model as an Infinite Promise (in fact, that's what this implementation does). However, in Fizz and Flight that doesn't work because the stream needs to end at some point. We don't have any way of knowing that we're suspended on infinite promises. It's not enough to tag the promises because you could await those and thus creating new promises. The only way we really have to signal this through a series of indirections like async functions, is by throwing. It's not 100% safe because these values can be caught but it's the best we can do. Effectively `postpone(reason)` behaves like a built-in [Catch Boundary](https://github.com/facebook/react/pull/26854). It's like `raise(Postpone, reason)` except it's built-in so it needs to be able to be encoded and caught by Suspense boundaries. In Flight and Fizz these behave pretty much the same as errors. Flight just forwards it to retrigger on the client. In Fizz they just trigger client rendering which itself might just postpone again or fill in the value. The difference is how they get logged. In Flight and Fizz they log to `onPostpone(reason)` instead of `onError(error)`. This log is meant to help find deopts on the server like finding places where you fall back to client rendering. The reason that you pass in is for that purpose to help the reason for any deopts. I do track the stack trace in DEV but I don't currently expose it to `onPostpone`. This seems like a limitation. It might be better to expose the Postpone object which is an Error object but that's more of an implementation detail. I could also pass it as a second argument. On the client after hydration they don't get passed to `onRecoverableError`. There's no global `onPostpone` API to capture postponed things on the client just like there's no `onError`. At that point it's just assumed to be intentional. It doesn't have any `digest` or reason passed to the client since it's not logged. There are some hacky solutions that currently just tries to reuse as much of the existing code as possible but should be more properly implemented. - Fiber is currently just converting it to a fake Promise object so that it behaves like an infinite Promise. - Fizz is encoding the magic digest string `"POSTPONE"` in the HTML so we know to ignore it but it should probably just be something neater that doesn't share namespace with digests. Next I plan on using this in the `/static` entry points for additional features. Why "postpone"? It's basically a synonym to "defer" but we plan on using "defer" for other purposes and it's overloaded anyway.
69 lines
2.6 KiB
JavaScript
69 lines
2.6 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
// ATTENTION
|
|
// When adding new symbols to this file,
|
|
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
|
|
|
|
// The Symbol used to tag the ReactElement-like types.
|
|
export const REACT_ELEMENT_TYPE: symbol = Symbol.for('react.element');
|
|
export const REACT_PORTAL_TYPE: symbol = Symbol.for('react.portal');
|
|
export const REACT_FRAGMENT_TYPE: symbol = Symbol.for('react.fragment');
|
|
export const REACT_STRICT_MODE_TYPE: symbol = Symbol.for('react.strict_mode');
|
|
export const REACT_PROFILER_TYPE: symbol = Symbol.for('react.profiler');
|
|
export const REACT_PROVIDER_TYPE: symbol = Symbol.for('react.provider');
|
|
export const REACT_CONTEXT_TYPE: symbol = Symbol.for('react.context');
|
|
export const REACT_SERVER_CONTEXT_TYPE: symbol = Symbol.for(
|
|
'react.server_context',
|
|
);
|
|
export const REACT_FORWARD_REF_TYPE: symbol = Symbol.for('react.forward_ref');
|
|
export const REACT_SUSPENSE_TYPE: symbol = Symbol.for('react.suspense');
|
|
export const REACT_SUSPENSE_LIST_TYPE: symbol = Symbol.for(
|
|
'react.suspense_list',
|
|
);
|
|
export const REACT_MEMO_TYPE: symbol = Symbol.for('react.memo');
|
|
export const REACT_LAZY_TYPE: symbol = Symbol.for('react.lazy');
|
|
export const REACT_SCOPE_TYPE: symbol = Symbol.for('react.scope');
|
|
export const REACT_DEBUG_TRACING_MODE_TYPE: symbol = Symbol.for(
|
|
'react.debug_trace_mode',
|
|
);
|
|
export const REACT_OFFSCREEN_TYPE: symbol = Symbol.for('react.offscreen');
|
|
export const REACT_LEGACY_HIDDEN_TYPE: symbol = Symbol.for(
|
|
'react.legacy_hidden',
|
|
);
|
|
export const REACT_CACHE_TYPE: symbol = Symbol.for('react.cache');
|
|
export const REACT_TRACING_MARKER_TYPE: symbol = Symbol.for(
|
|
'react.tracing_marker',
|
|
);
|
|
export const REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED: symbol = Symbol.for(
|
|
'react.default_value',
|
|
);
|
|
|
|
export const REACT_MEMO_CACHE_SENTINEL: symbol = Symbol.for(
|
|
'react.memo_cache_sentinel',
|
|
);
|
|
|
|
export const REACT_POSTPONE_TYPE: symbol = Symbol.for('react.postpone');
|
|
|
|
const MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|
const FAUX_ITERATOR_SYMBOL = '@@iterator';
|
|
|
|
export function getIteratorFn(maybeIterable: ?any): ?() => ?Iterator<any> {
|
|
if (maybeIterable === null || typeof maybeIterable !== 'object') {
|
|
return null;
|
|
}
|
|
const maybeIterator =
|
|
(MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL]) ||
|
|
maybeIterable[FAUX_ITERATOR_SYMBOL];
|
|
if (typeof maybeIterator === 'function') {
|
|
return maybeIterator;
|
|
}
|
|
return null;
|
|
}
|