diff --git a/src/content/reference/react/use.md b/src/content/reference/react/use.md index dc43fa2289c..ffbb32f5943 100644 --- a/src/content/reference/react/use.md +++ b/src/content/reference/react/use.md @@ -436,6 +436,277 @@ To use the Promise's `catch` method, call + +#### Passing a basic Promise {/*passing-a-basic-promise*/} + + + +```js src/App.js active +import { Suspense, use, useState } from "react"; + +function preloadUser(id) { + // This is not a real implementation of getting the + // Promise for the user. A real implementation would + // probably call `fetch` or another data fetching method. + // The actual implementation should cache the Promise. + const promise = Promise.resolve(`User #${id}`); + + return promise; +} + +function UserDetails({ userUsable }) { + const user = use(userUsable); + return

Hello, {user}!

; +} + +export default function App() { + const [userId, setUserId] = useState(null); + // The initial + const [userUsable, setUser] = useState(null); + + return ( +
+ + + Loading

}> + {userUsable ? ( + + ) : ( +

No user selected

+ )} +
+
+ ); +} +``` + +
+ + + +#### Passing the Promise with a `status` field {/*passing-the-promise-with-the-status-field*/} + + + + +```js src/App.js active +import { Suspense, use, useState } from "react"; +import { flushSync } from "react-dom"; + +class PromiseWithStatus extends Promise { + status = "pending"; + value = null; + reason = null; + + constructor(executor) { + let resolve; + let reject; + super((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + // Setting the `status` field allows React to + // synchronously read the value if the Promise + // is already settled by the time the Promise is + // passed to `use`. + executor( + (value) => { + this.status = "fulfilled"; + this.value = value; + resolve(value); + }, + (reason) => { + this.status = "rejected"; + this.reason = reason; + reject(reason); + } + ); + } +} + +function preloadUser(id) { + // This is not a real implementation of getting the + // Promise for the user. A real implementation would + // probably call `fetch` or another data fetching method. + // The actual implementation should cache the Promise. + // The important part is that we are using the + // PromiseWithStatus subclass here. Check out the next + // step if you're not controlling the Promise creation + // (e.g. when `fetch` is used). + const promise = PromiseWithStatus.resolve(`User #${id}`); + + return promise; +} + +function UserDetails({ userUsable }) { + const user = use(userUsable); + return

Hello, {user}!

; +} + +export default function App() { + const [userId, setUserId] = useState(null); + // The initial + const [userUsable, setUser] = useState(null); + + return ( +
+ + + Loading

}> + {userUsable ? ( + + ) : ( +

No user selected

+ )} +
+
+ ); +} + +``` + +
+ + + +#### Simplified implementation setting the `status` field {/*simplified-implementation-setting-the-status-field*/} + + + +```js src/App.js active +import { Suspense, use, useState } from "react"; +import { flushSync } from "react-dom"; + +function preloadUser(id) { + const value = `User #${id}`; + // This is not a real implementation of getting the + // Promise for the user. A real implementation would + // probably call `fetch` or another data fetching method. + // The actual implementation should cache the Promise. + const promise = Promise.resolve(value); + + // We don't need to create a custom subclass. + // We can just set the necessary fields directly on the + // Promise. + promise.status = "pending"; + promise.then( + (value) => { + promise.status = "fulfilled"; + promise.value = value; + }, + (error) => { + promise.status = "rejected"; + promise.reason = error; + } + ); + + // Setting the status in `.then` is too late if we want + // to create an already settled Promise. We only included + // setting the fields in `.then` for illustration + // purposes. Since our demo wants an already resolved + // Promise, we set the necessary fields synchronously. + promise.status = "fulfilled"; + promise.value = value; + return promise; +} + +function UserDetails({ userUsable }) { + const user = use(userUsable); + return

Hello, {user}!

; +} + +export default function App() { + const [userId, setUserId] = useState(null); + // The initial + const [userUsable, setUser] = useState(null); + + return ( +
+ + + Loading

}> + {userUsable ? ( + + ) : ( +

No user selected

+ )} +
+
+ ); +} +``` + +
+ + + + + ## Troubleshooting {/*troubleshooting*/} ### "Suspense Exception: This is not a real error!" {/*suspense-exception-error*/}