How do I return an observable from a computed? #3953
-
Hi, the documentation states not to do this, but if I really need to, how should I? An equivalent example to what I'm trying to do is convert a promise to an observable: function fetch(url: string|undefined): Promise<any> {
const result = observable.object({resolved: false, value: undefined, error: undefined});
fetch(this.url).then((value) => {
runInAction(() => {
Object.assign(result, {resolved: true, value: value});
});
}).catch((e) => {
runInAction(() => {
Object.assign(result, {resolved: true, error: e});
});
});
return result;
}
class WebRequest {
url: string;
constructor(url: string|undefined){
this.url = url;
makeObservable(this, {
url: observable,
value: computed,
});
}
get value(){
return fetch(this.url);
}
}
const requestUrls = ['http://my.api.com', 'http://my.api.com/1', ...];
const request = new WebRequest();
autorun(() => {
if (request.value.resolved){
console.log(request.value)
if (requestUrls.length){
request.url = requestUrls.shift();
}
}
}) Basically the idea is to transform an imperative (i.e. fetch a url) to a declarative (i.e. data which fills out appropriately) and in order to do this, I need the value field of the request to update when the request resolves and also fetch a new request when the url changes. The problem here is that the update that happens in the fetch is triggering the computed to rerun which is triggering a new request creating an infinite loop. I need the "fetch" function to return an observable, but I can change the contents of WebRequest. What is the proper way to do this? Thanks! Note: I have seen #3136 but that seems unrelated to this question. |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 3 replies
-
computeds are supposed to be side effect free, which your example clearly isn't, as the whole point of it is to make a side effect. Instead, set up a reaction in the constructor (or on the first read of a not computed getter), that reacts to your input URL and initiates the fetch, and stores the result in a local observable. Something like Please note that this setup is still suspectable to synchronization issues, so you probably want to make sure that it was the latest fetch you issued that is currently resolving, before updating |
Beta Was this translation helpful? Give feedback.
-
I don't really got the question, but computeds and reactions automatically
subscribe to everything you right in them. So if your computed runs () => x
+ y, it will observe those two.
…On Tue, Oct 29, 2024, 9:11 PM ofersadgat ***@***.***> wrote:
First of all, thank you so much for the great replies and especially the
speed. Sorry, in order to give you a more full picture of what I'm trying
to build, I'll explain more. I am trying to build an ORM which has a query
language. That query language has boolean functions (e.g. "x + y") and I
want each of the inputs to the boolean function to be able to be an
observable, which, if it changes, will cause the result to be recomputed.
For example, if I do:
const myvariable = observable.object({foo: 4});const result = new BooleanFunction("$myvariable.foo * 2", {myvariable: myvariable});console.log(result.value); // 8myvariable.foo = 5;console.log(result.value); // 10
now, my above example comes into play in the same way:
const myvariable = observable.object({foo: 4});const result = new BooleanFunction("$myvariable.foo * fetch('http://foo.com/api/1')", {myvariable: myvariable});console.log(result.value); // undefined// later (assuming the result of the fetch is 5)console.log(result.value); // 10
Lastly, boolean functions should be able to be chained together. I think
your above idea of using onBecomeUnobserved will mostly work for the
described use case, but I think this might run into trouble if I dont know
what the dependencies are in the reaction constructor, right? E.g. if the
boolean function were to be cross join, it would be depending on the actual
values in the left and not just the array value itself. Any idea for how to
solve this?
—
Reply to this email directly, view it on GitHub
<#3953 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAN4NBHURR2WPCUMPEGJTQTZ57TXFAVCNFSM6AAAAABQY5T5RGVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCMBZGIYTANI>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
computeds are supposed to be side effect free, which your example clearly isn't, as the whole point of it is to make a side effect. Instead, set up a reaction in the constructor (or on the first read of a not computed getter), that reacts to your input URL and initiates the fetch, and stores the result in a local observable. Something like
reaction(() => this.url, url => fetch(url).then(value => Object.assign(this.value, { resolved:true, value })
Please note that this setup is still suspectable to synchronization issues, so you probably want to make sure that it was the latest fetch you issued that is currently resolving, before updating
this.value
.