-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
Ember-resources example with composition of remoteData with async authorization headers? #1187
Comments
ok, I guess I could use request = trackedFunction(this, async () => {
const token = await getAuth().currentUser?.getIdToken()
const headers = new Headers({"Authorization": `Bearer ${token}`})
let response = await fetch(this.args.fetchUrl, {headers});
let data = await response.json();
return data;
});
@use latest = keepLatest({
value: () => this.request.value,
when: () => this.request.isLoading,
}); An example with a combination of |
I love this suggestion! I think what I want to provide a way to do is something that ultimately looks like this, keeping in mind that classes are not the primary way to use resources, but only one of a few. const Request = resourceFactory((urlOrUrlFn: string | () => string) => {
return resource(({ use }) => {
let tokenRequest = use(TrackedFunction(async () => getAuth()?.currentUser?.getIdToken()));
if (tokenRequest.isLoading) {
return tokenRequest; // a State<string>
}
let headers = new Headers({ Authorization: `Bearer ${tokenRequest.value}` });
let actualRequest = use(RemoteData(urlOrUrlFn, { headers }));
return actualRequest; // State<unknown>, I think
// the states are different, but I think they both have isLoading and 'value'
// these should probably be the same State, so you can be certain of all the
// promise-handling properties being the same so consumer-facing usage
// is cleaner
});
});
export class Demo {
@use request = Request(() => this.args.fetchUrl));
@use latest = keepLatest({
value: () => this.request.value,
when: () => this.request.isLoading,
});
} This is some psuedo api as |
This has been implemented in ember-resources -- idk if you want to write up tutorial text? could be helpful for others! |
ok, great, thanks. I will give it a go. I ended up having a little issue with
I have some expensive downstream calculations and rendering that takes effect when I can think of two solutions:
Thanks, |
this is as optimized as it can be 🎉
The path here is to handle the non-value case, which you already had to do, but was hidden! (and often forgotten!) @cached
get expensiveCalculation() {
if (!this.latest) {
// or whatever the initial value
return null; // or {} or [] or whatever is appropriate
}
return doSomethingExpensive();
}
Correct, additionally, ember-concurrency setting properties introduces "hidden states" -- you still have a period of time where your values don't have what you want (until the appropriate thing is awaited), but it's the same situation, just wrapped differently, with implicit states.
yeah, it's kind of the same as |
@NullVoxPopuli thanks for your reply. Yes, I guess checking for To avoid subsequent double calculations/renders, I feel a need to do track the if @fetchUrl (e.g. tracked from dates in query params) is unequal to the last used @fetchUrl, and only then invalidate RemoteData component: @tracked lastFetchedUrl?: string;
@tracked lastData? : any[]
fetchRemoteData = task(async () => {
const url = this.args.fetchUrl
if (url === this.lastFetchedUrl) return
const response = await fetch(url)
const json = await response.json()
this.lastFetchedUrl = url
this.lastData = json
})
<template>
{{did-insert (perform this.fetchRemoteData) }}
{{did-update (perform this.fetchRemoteData) @fetchUrl}}
{{yield this.lastData this.lastFetchedUrl}}
</template>
} <RemoteData
@fetchUrl={{this.fetchUrl}}
as |data lastFetchedUrl|
>
<ExpensiveCalculation
@data={{data}}
as |calculatedData|
/>
<ExpensiveRender
@data={{calculatedData}}
/>
<ExpensiveCalculation>
</RemoteData> This works, but it feels wrong to work against the grain of reactive patterns. But perhaps it has to be done imperatively for full control. ? |
the pattern you describe is specific, and makes sense that you're running in to a bit of a rough edge with some primitives. "Keeping latest" is what I've called what you're doing: tutorial page | repl Because you want to only update a value when you have a new value to update with, and ignore all the intermediate stuff. So, to adapt your example code: class Demo {
@use currentRequest = RemoteData(() => this.args.fetchUrl));
@use data = keepLatest({
value: () => this.request.value,
when: () => this.request.isLoading,
});
<template>
{{yield this.data}}
</template>
} I like this pattern because it allows you to still indicate loading or error state while keeping your data displayed |
Hi. Thanks for the fun and useful https://tutorial.glimdown.com.
When looking at the remote-data and keeping latest examples I get exited about the simplicity and composability.
However, I struggled to wrap my head around good practices with async inputs for the remoteData function. In my case I use firebase getIdToken , which returns a promise.
Ideally I'd like to compose a RemoteData function that awaits the results of getIdToken().
However, simply marking the function and awaiting the token, makes
this.myData
return asPromise<State<string>>
, not asState<string>
, so I'm probably on the wrong tracks here.Any ideas for better composibilbity? Wrapping it in a resourceFactory? Or do I anyways need to create a custom wrapper util that returns a remoteData like State if getIdToken is running?
Posting here, since I think it could be a useful exercise in the glimdown tutorial. Thanks!
The text was updated successfully, but these errors were encountered: