Skip to content

Commit 40ace05

Browse files
committed
Docs for Promise subclasses
1 parent f9e2c13 commit 40ace05

File tree

1 file changed

+82
-0
lines changed
  • src/content/reference/react

1 file changed

+82
-0
lines changed

src/content/reference/react/use.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,88 @@ To use the Promise's <CodeStep step={1}>`catch`</CodeStep> method, call <CodeSte
436436
437437
---
438438
439+
### Avoiding fallbacks by passing Promise subclasses {/*avoiding-fallbacks-by-passing-promise-subclasses*/}
440+
441+
React will read the `status` field on Promise subclasses to synchronously read the value without waiting for a microtask. If the Promise is already settled (resolved or rejected), React can read the value immediately without suspending and showing a fallback if the update was not part of a Transition (e.g. [`ReactDOM.flushSync()`](/reference/react-dom/flushSync)).
442+
443+
React will set the `status` field itself if the passed Promise does not have this field set. Suspense-enabled libraries should set the `status` field on the Promises they create to avoid unnecessary fallbacks. Calling `use` conditionally depending on whether a Promise is settled or not is discouraged. `use` should be called unconditionally so that React DevTools can show that the Component may suspend on data.
444+
445+
<Sandpack>
446+
447+
```js src/App.js active
448+
import { Suspense, use, useState } from "react";
449+
import { preload } from "./data-fetching.js";
450+
451+
function UserDetails({ userUsable }) {
452+
const user = use(userUsable);
453+
return <p>Hello, {user}!</p>;
454+
}
455+
456+
export default function App() {
457+
const [userId, setUserId] = useState(null);
458+
// The initial
459+
const [userUsable, setUser] = useState(null);
460+
461+
return (
462+
<div>
463+
<p>
464+
Passing the Promise without the <code>status</code> field will show the
465+
fallback because the Promise resolves in the next microtask.
466+
</p>
467+
<button
468+
onClick={() => {
469+
setUser(Promise.resolve("User #2"));
470+
setUserId(2);
471+
}}
472+
>
473+
Load Promise not integrated with Suspense
474+
</button>
475+
<button
476+
onClick={() => {
477+
setUser(preload(1));
478+
setUserId(1);
479+
}}
480+
>
481+
Load Promise integrated with Suspense
482+
</button>
483+
<Suspense key={userId} fallback={<p>Loading user...</p>}>
484+
{userUsable ? (
485+
<UserDetails userUsable={userUsable} />
486+
) : (
487+
<p>No user selected</p>
488+
)}
489+
</Suspense>
490+
</div>
491+
);
492+
}
493+
```
494+
495+
496+
```js src/data-fetching.js
497+
export function preload(id) {
498+
// This is not a real implementation of getting the Promise for the user.
499+
// The actual implementation should cache the Promise
500+
const promise = Promise.resolve(`User #${id}`);
501+
502+
// Setting the `status` field allows React to synchronously read
503+
// the value if the Promise is already settled by the time the Promise is passed to `use`.
504+
promise.status = "pending";
505+
promise.then(
506+
(value) => {
507+
promise.status = "fulfilled";
508+
promise.value = value;
509+
},
510+
(error) => {
511+
promise.status = "rejected";
512+
promise.reason = error;
513+
},
514+
);
515+
return promise;
516+
}
517+
```
518+
519+
</Sandpack>
520+
439521
## Troubleshooting {/*troubleshooting*/}
440522
441523
### "Suspense Exception: This is not a real error!" {/*suspense-exception-error*/}

0 commit comments

Comments
 (0)