-
Notifications
You must be signed in to change notification settings - Fork 562
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
Promise-returning setState and forceUpdate #10
Conversation
|
||
- If `setState` without the callback is (or could be) optimized in anyway (due to not | ||
needing the schedule the callback or something), it wouldn't be able to anymore because | ||
the promise would always have to be returned. |
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.
"Yes, we’d have to tag it as a node to visit when committing changes. setState() without callback doesn’t need that." – Dan
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.
Depending on how big of an impact this makes, I would consider this a big enough reason to reject this proposal outright.
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.
It may be possible to detect if setState
is declared async (which is not the same as returning a promise, true, but perhaps a good enough work-around?) - e.g. https://stackoverflow.com/questions/38508420/how-to-know-if-a-function-is-async
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.
Some promisified callback APIs (like the AWS SDK) use a call to .promise()
to know whether to ~~~return a Promise~~~ create a Promise.
await this.setState({ loading: true }).promise(); // awaitable/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.
That still means that you'd have to return an object that has a promise
method, and setState
always has to call a hidden callback to notify the object just in case promise
was called. You still have to do all the same bookkeeping; the only difference is whether you make a Promise
instance or not.
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 the bigger concern is more:
let thenable = this.setState({ ... });
setImmediate(() => {
thenable.then(() => {
// ...
});
});
What if .then
isn't called immediately (but is potentially called before it would have resolved)?
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.
Same as calling this.setState(null, callback)
.
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.
Is that going to work as expected (i.e. the promise will resolve after the explicit setState
finishes)? If so, could .then()
always have those semantics?
If so, you could potentially only allocate at most a single thenable per component instance.
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.
Same as calling
this.setState(null, callback)
I don't see how that's possible unless scheduleCallback
can be called after the update is already applied
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.
unless scheduleCallback can be called after the update is already applied
Bingo :D
This seems easy enough to provide in an external library, and therefore be entirely opt-in. export const setStatePromise = (o, state) => new Promise(resolve => o.setState(state, resolve));
await setStatePromise(this, { loading: true })
// or if you want syntax similar to the RFC, but still opt-in for performance
this.setStateAsync = setStatePromise.bind(null, this) // in constructor
await this.setStateAsync({ loading: true }) We should be careful with naming conventions as well, now that |
I think that proposal will cause a perfomance problems. |
One data point here. In our concurrent mode API where you want to do one setState first at a higher priority followed by a setState at lower priority we originally proposed something like deferring a callback (or just requestIdleCallback or setTimeout). However, that introduced an unfortunate async seam that could now introduce new imperative effects (like other setStates). That in turn causes the sequence to be scheduled in a non-predictable way. Instead, we now propose the "setState(); scheduler.next(() => { setState(); })" pattern which just changes the batching/priorities but the whole thing is added to the queue synchronously so that subsequent events are stacked on top of the sequence instead of interleaving. The original example in the beginning of this RFC is actually a good example of this. The classic problem with code like this is that it's not resilient to new updates coming in - in the meantime. E.g. what if props updates? How do fetches that happen during update interleave with other updates happening during the async sequence? For data fetching in general, we have the whole Suspense thing which can solve most of the canonical use cases for this. For the more general problem, I think we need something that defines a sequence up front instead of triggering imperative code along the way. That way if other imperative code adds a sequence, it will by default be added in full after the first sequence instead of some operations becoming interleaved. Then those can be combined to be executed in different priorities or collapse like our update queue generally works. We've learned a lot since this RFC was originally posted. I'm now pretty skeptical that this exact pattern is the right way forward. It's (deceiving) simplicity is aspirational for any API to compete with though. |
Can someone update about this RFC? This is a fairly easy and straight-forward suggestion, why is it still open? |
This rfc predates hooks, and I don't really think it's worth improving the class api anymore, I'm gonna close it |
[Rendered]
This RFC specifies
setState
andforceUpdate
to return a promise when no callback is provided.