Skip to content

Commit e29f977

Browse files
committed
Docs for Promise subclasses
1 parent f9e2c13 commit e29f977

File tree

1 file changed

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

1 file changed

+81
-0
lines changed

src/content/reference/react/use.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,87 @@ 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 activeimport { Suspense, use, useState } from "react";
448+
import { preload } from "./data-fetching.js";
449+
450+
function UserDetails({ userUsable }) {
451+
const user = use(userUsable);
452+
return <p>Hello, {user}!</p>;
453+
}
454+
455+
export default function App() {
456+
const [userId, setUserId] = useState(null);
457+
// The initial
458+
const [userUsable, setUser] = useState(null);
459+
460+
return (
461+
<div>
462+
<p>
463+
Passing the Promise without the <code>status</code> field will show the
464+
fallback because the Promise resolves in the next microtask.
465+
</p>
466+
<button
467+
onClick={() => {
468+
setUser(Promise.resolve("User #2"));
469+
setUserId(2);
470+
}}
471+
>
472+
Load Promise not integrated with Suspense
473+
</button>
474+
<button
475+
onClick={() => {
476+
setUser(preload(1));
477+
setUserId(1);
478+
}}
479+
>
480+
Load Promise integrated with Suspense
481+
</button>
482+
<Suspense key={userId} fallback={<p>Loading user...</p>}>
483+
{userUsable ? (
484+
<UserDetails userUsable={userUsable} />
485+
) : (
486+
<p>No user selected</p>
487+
)}
488+
</Suspense>
489+
</div>
490+
);
491+
}
492+
```
493+
494+
495+
```js src/data-fetching.js
496+
export function preload(id) {
497+
// This is not a real implementation of getting the Promise for the user.
498+
// The actual implementation should cache the Promise
499+
const promise = Promise.resolve(`User #${id}`);
500+
501+
// Setting the `status` field allows React to synchronously read
502+
// the value if the Promise is already settled by the time the Promise is passed to `use`.
503+
promise.status = "pending";
504+
promise.then(
505+
(value) => {
506+
promise.status = "fulfilled";
507+
promise.value = value;
508+
},
509+
(error) => {
510+
promise.status = "rejected";
511+
promise.reason = error;
512+
},
513+
);
514+
return promise;
515+
}
516+
```
517+
518+
</Sandpack>
519+
439520
## Troubleshooting {/*troubleshooting*/}
440521
441522
### "Suspense Exception: This is not a real error!" {/*suspense-exception-error*/}

0 commit comments

Comments
 (0)