-
Notifications
You must be signed in to change notification settings - Fork 47.2k
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
Support for asynchronous values (like Promises and Observables) #6481
Comments
Hey @joelrich, I’m fairly certain that this proposal runs counter to some of the goals the project has and the direction it is headed. Specifically around scheduling. While not a direct answer, you can glean some of the thoughts surrounding this in the following threads: Unable to find a source, default react state is natively serializeable and synchronous right now. This allows React to reason about and have tight control over reconciliation and rendering. This also allows React to decide on scheduling. Promises as an abstraction remove all of these properties making current and future optimizations difficult or impossible. |
Hi @iamdustan, Do you see another way to improve the support for promises - especially for use cases with libraries like Falcor where most values in the view are promises? Or do you think the current solution is good enough? I do not think that the scheduling is a big problem. Similar to then + setState, you would only have to schedule an update if the promise is resolved/rejected. What exactly do you mean with "react state is natively serializeable"? For example, if you put an object of an ES6 class into the state, is the state then still serializable? Sure, you can |
cc @sebmarkbage for some additional thoughts / links. |
don't know if this helps but see https://github.com/heroku/react-refetch (specifically https://github.com/heroku/react-refetch/blob/master/src/PromiseState.js) for a serialisable, immutable representation of the state of a Promise that you can use to stand in for a promise in a way that makes sense inside a |
This has been discussed so many times that there is no single easy link to post to. We should probably make a new summary but I'll do my best here. We did have an idea to allow promises passed to
Now there are mitigating factors for all these things:
|
@sebmarkbage, thank you for the summary. My take on 1) is that the promise function would allow to specify a loading value. Example:
Alternatively, you could also wrap multiple elements with another helper function to wait until all promises are resolved. Since this function would just return another promise, this would not have to be part of React as long as it is allowed to return a (wrapped) promise from render(). Regarding 3), I am still not sure if I understand the problem. When the promise is resolved, the behaviour could be very similar to |
@joecritch How about you have a component which can render a promise? Something like, <PromisedValue
promise={promise}
renderLoading={() => <...>}
renderItem={value => <...>}
renderError={err => <...>}
/> Should be easy enough to write such a component. |
@satya164 unfortunately, this does not work for properties. |
Do you think API like this could make any sense?
I imagine it might be a bit chaotic, but main idea here is that render function is generator and it is able to Maybe it could be 'next level' of declarative nature of React. Right now you are able to be declarative when describing how UI looks basing on current data - with such approach you could be able to be declarative when describing everything that can happen during component lifetime. More complex scenarios could look like:
In such component, it's really obvious not only how component will look at any state, but also what is next possible thing that could happen. In current version of React, you only see what are possible scenarios, not what are possible sequences of them. |
omg, no sense in a today world. better would be to use async: class UserData extends Component {
render() {
return
<div>
<h2>Good morning</h2>
renderAsync(
async () => {
const userData = await whenDidMount(fetchUser(this.props.userId));
return <div>Hello, {userData.username}</div>
},
() =><div>Loading...</div>,
(e) =><div>error</div>
)
</div>
}
} |
The heuristic i settled on for handling transient states in DIO was to allow Promises as both render return types & element types, and additionally allow a Promise component to describe children that render before it resolves. For example. const Something = Promise.resolve(<ul><li>Something</li></ul>)
render(
<Something>
<h1>Loading</h1>
<span>...</span>
</Something>
) |
Second part of my talk (about “suspense”) is relevant to this: https://reactjs.org/blog/2018/03/01/sneak-peek-beyond-react-16.html |
I’m going to close this because Suspense API is designed exactly for this purpose, and it has been merged into master. It’s not going to be available or usable until later this year but there’s no need to keep this issue open if we have an initial solution in the codebase and will keep iterating on it. When it’s fully ready you’ll read it on the React blog. |
Just to follow up on this.
As noted earlier, we solved this with
We've implemented this in React 18 (using "concurrent rendering"). For example, As for the original request, reactjs/rfcs#229 addresses it. It just took us six years to figure out how to do it right. |
At the moment, it is quite cumbersome to work with promises in React. Either you write a lot of code to update the state inside the
then()
callbacks or you use a library like react-promise which only works for children (not properties) because it is based on components.Another option is to traverse the whole virtual dom and replace promises before they are passed to React. (I have read once a blog post about this but cannot find it any longer.) This is obviously not ideal for the rendering performance.
Since promises are now a standard, I think there should be simpler way to use them directly in the view. One option would be to introduce an AsyncValue type with a Promise implementation. The AsyncValue type would then be supported for children as well as properties.
Promises could then simply be used like this:
Opposed to supporting promises everywhere (directly without the promise function), this should not have unexpected side effects. Moreover, other types of asynchronous values (for example Observables) could be added later easily just by creating another AsyncType implementation.
Angular 2 takes a similar route with the async pipe. There you can simply write
in attributes as well as normal content.
This feature would also be very convinient for libraries like Flux because it allows to fetch properties directly inside the view and avoids the reptition which you have with Relay:
What do you think about this proposal?
Has this been discussed before? I couldn't find a thread which covers this particular topic.
The text was updated successfully, but these errors were encountered: