dillon1000/react
Commit
Browse files Add ReactDOM `browser()` API (#37143)
## Summary
Adds a new API to `react-dom` called `browser()`.
`browser()` returns a "usable" that will error during SSR and resolve
during rendering in the browser. The purpose is to allow you to express
the idea that a component should suspend on the server but not in the
browser. The method is not available inside a `react-server`
environment. This is a client only feature.
This is a `react-dom` API because the concept of browser doesn't apply
generally to React itself.
This codifies a pattern that is common in some apps where you error
during SSR to prevent rendering some component on the server and you end
up suppressing the error that is reported in the client to avoid this
appearing like a problem rather than intended behavior. Unfortunately
this is not an option for many because hacking around to prevent errors
from being logged is not practical for many
By making this a React API we enabled this common pattern in any React
using library or application
```tsx
import {use, Suspense} from 'react';
import {browser} from 'react-dom';
function BrowserOnly() {
use(browser());
return <ClientContent />;
}
function App() {
return (
<Suspense fallback={<Fallback />}>
<BrowserOnly />
</Suspense>
);
}
```
It is an error to `use(browser())` outside of a Suspense boundary
because you cannot recover from the root. this restriction may be lifted
in the future but is part of the current limitations of the API
## Implementation
Deferring rendering to a downstream system is modeled in React already
as recoverable errors. The idea is that in some environments you might
not want to report something directly as an error because a later
environment has an opportunity to recover from it without alerting the
user to the mishap. This concept also shows up in RSC with halted
references. They can "recover" in a later render by eventually resolving
to some value.
To model the idea of "render in the browser" we are really just modeling
an intentional recoverable error. However since you don't want to treat
this kind of error as exceptional we intentionally suppress logging.
Additionally since aborting a server render is semantically equivalent
to "erroring" in every unfinished task we also support aborting with a
`browser()` so you can describe ending a stream with intentional holes
that won't be logged as errors in the browser when hydrating.
One interesting thing we do with this particular API is it returns an
object that is isomorphic and it's the `use` or `abort` function that
handles differing behaviors. This means you can create these objects in
module scope and use them even in complex scenarios like server
rendering inside the browser while React is rendering.
This implementation is flagged so we can disable the feature quickly if
we decide to not ship this in a stable. It is going into React
unprefixed for now because the semantics are clear and the utility is
widely known.
## Alternatives
We considered `useBrowser()` or a similar hook however this means you
must call it unconditionally. There are use cases where props might
influence whether you want to allow something to render during SSR or
not. for instance you might have a data fetching library that accepts
initial data on the server but if it doesn't receive initial data it
falls back to browser only rendering.
Another consideration is a throwing function like just calling
`browser()` would throw if called during an SSR render. The main reason
we do not think this is a good idea is because you can then call this
arbitrarily deep and the throw can be caught and might be suppressed
accidentally. By making it a usable it can only be done in hooks or
hook-like contexts.Changed paths28 files
First-parent comparisonpackages/react-debug-tools/src/ReactDebugHooks.js ModifiedM packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js ModifiedM packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetInlineCodeStrings.js ModifiedM packages/react-dom-bindings/src/server/fizz-instruction-set/ReactDOMFizzInstructionSetShared.js ModifiedM packages/react-dom/index.js ModifiedM packages/react-dom/src/ReactDOMFB.js ModifiedM packages/react-dom/src/ReactDOMFB.modern.js ModifiedA packages/react-dom/src/__tests__/ReactDOMBrowser-test.js AddedM packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js ModifiedM packages/react-dom/src/client/ReactDOMClientFB.js ModifiedM packages/react-dom/src/shared/ReactDOM.js ModifiedA packages/react-dom/src/shared/ReactDOMBrowser.js AddedM packages/react-reconciler/src/ReactFiberBeginWork.js ModifiedM packages/react-reconciler/src/ReactFiberHooks.js ModifiedM packages/react-server/src/ReactFizzHooks.js ModifiedM packages/react-server/src/ReactFizzServer.js ModifiedM packages/react-server/src/ReactFlightHooks.js ModifiedM packages/shared/ReactFeatureFlags.js ModifiedA packages/shared/ReactRecoverable.js AddedM packages/shared/ReactSymbols.js ModifiedM packages/shared/ReactTypes.js ModifiedM packages/shared/forks/ReactFeatureFlags.native-fb.js ModifiedM packages/shared/forks/ReactFeatureFlags.native-oss.js ModifiedM packages/shared/forks/ReactFeatureFlags.test-renderer.js ModifiedM packages/shared/forks/ReactFeatureFlags.test-renderer.native-fb.js ModifiedM packages/shared/forks/ReactFeatureFlags.test-renderer.www.js ModifiedM packages/shared/forks/ReactFeatureFlags.www.js ModifiedM scripts/error-codes/codes.json ModifiedPatch
Files changed
Rendering syntax-highlighted changes…