-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Docs for Promise subclasses #8125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Docs for Promise subclasses #8125
Conversation
e29f977 to
40ace05
Compare
Size changes📦 Next.js Bundle Analysis for react-devThis analysis was generated by the Next.js Bundle Analysis action. 🤖 This PR introduced no changes to the JavaScript bundle! 🙌 |
src/content/reference/react/use.md
Outdated
| ### Avoiding fallbacks by passing Promise subclasses {/*avoiding-fallbacks-by-passing-promise-subclasses*/} | ||
| 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)). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add some code here for a high-level view of what this looks like?
const dataPromise = {
then: () => {},
status: 'fulfilled',
value: `some data'
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we want to show how to create a custom thenable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea while you can use a custom Thenable, it's probably recommended to just use a native Promise for this.
You can make it a sub-class like such:
class FulfilledPromise extends Promise {
status = 'fulfilled';
value = null;
constructor(data) {
super(resolve => resolve(data));
this.value = data;
}
}
const promise = new FulfilledPromise(data);This makes it more palatable for sticklers that thinks adding expandos to native objects is bad. It shows that the pattern is encouraged by the platform. If it wasn't, then why can I sub-class natives? If can't add fields, what am I supposed to do with those sub-classes?
But in practice when you're not adding any methods to the prototype this is really just the same thing as:
const promise = Promise.resolve(data);
promise.status = 'fulfilled';
promise.value = data;Which is smaller code and faster, so why not do that? It also doesn't risk getting compiled which would break native subclassing.
So maybe document one stickler version and one faster version (and hint that thenables might be even faster).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
| 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. | ||
| <Sandpack> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do something like this instead:
And structure it so that the most relevant code (the promise creation) is what's visible first?
Like this you should see this:
With a Promise subclass
function getUser(id) {
return {
status: 'fulfilled',
value: `User #${id}`,
then: () => {}
}
}
function UserDetails() {
const user = use(getUser());
return <p>Hello, {user}!</p>;
}Without a Promise subclass
function getUser(id) {
return Promise.resolve(`User #${id}`);
}
function UserDetails() {
const user = use(getUser());
return <p>Hello, {user}!</p>;
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
src/content/reference/react/use.md
Outdated
| ### Avoiding fallbacks by passing Promise subclasses {/*avoiding-fallbacks-by-passing-promise-subclasses*/} | ||
| 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)). | ||
| 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is great content and very helpful!
It's a pretty advanced topic though so maybe point out the intended audience early? Maybe lead with:
If you are implementing a Suspense-enabled library, you can help React avoid unnecessarily suspending when you know the promise has already settled, by using
statusandvalueorreasonfields.
I think "subclass" and "microtask" is likely language that will trip people up, but if it's clear this is an advanced topic I think that's fine.
Maybe be a bit more explicit about the rejected/reason case as well?
Calling
useconditionally depending on whether a Promise is settled or not is discouraged.useshould be called unconditionally so that React DevTools can show that the Component may suspend on data.
I think this should be its own paragraph as it's a separate point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
Turning posts into docs
I.e. how React leverages
promise.status. Targeted at Suspense-enabled libraries.Preview