Comments (7)
ok, I guess I could use trackedFunction
, but then I'd have to implement the AbortController
myself.
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 trackedFunction
and remoteData
could be powerful, I guess.
from limber.
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 TrackedFunction
doesn't exist in a way that is compatible with use
yet -- but soon!
from limber.
This has been implemented in ember-resources -- idk if you want to write up tutorial text? could be helpful for others!
https://github.com/NullVoxPopuli/ember-resources/blob/main/ember-resources/CHANGELOG.md#640
from limber.
ok, great, thanks. I will give it a go.
I ended up having a little issue with latest
being returned twice, though:
- First when the resource is first needed (e.g. from a get())
- Then after the actualRequest is returned.
I have some expensive downstream calculations and rendering that takes effect when latest
is updated. Is there any good practice to make sure that latest
downstream calculations are only triggered on 2), not 1)?
I can think of two solutions:
- An ember-concurrency task where both the fetching and the expensive calculations are in a nested task, then updating a
@tracked
property only at the end to trigger a re-render. (This is in practice an imperative pattern, not reactive, i guess) - Investigate the Formula concept in starbeam.js. Do you have any experience how ready that is? I see there are some mentions of it in the ember-resource docs, but not sure how intentional that is.
Thanks,
from limber.
with latest being returned twice, though:
this is as optimized as it can be 🎉
(and found out the two values 💪 )
Is there any good practice to make sure that latest downstream calculations are only triggered on 2), not 1)?
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();
}
This is in practice an imperative pattern, not reactive, i guess
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.
Investigate the Formula concept in starbeam.js. Do you have any experience how ready that is?
yeah, it's kind of the same as @cached
getters. so @cached
is likely what you want
from limber.
@NullVoxPopuli thanks for your reply.
Yes, I guess checking for !this.latest
helps to avoid heavy double renders/calculations upon the first fetch, however caching the getter does not help, since subsequent fetchUrl invalidations rerun the getter every time this.latest
is invalidated by fetchUrl. (I have some queryParams are that have {refreshModel: true} (for instance dates), and all queryParams seem to be invalidated when the model refreshes.)
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 data
imperatively:
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>
}
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. ?
from limber.
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
from limber.
Related Issues (20)
- Get JSBin working
- try https://twitter.com/oscard0m_/status/1690664760268763136
- UI/UX: move help into a drawer
- Make Ember (the framework) Tutorial using embedded StackBlitz SDK
- [repl, the library] Add support for rendering *any* codefence
- Popover has lower z-index than resize-handle HOT 1
- Add Tutorial Chapter: How to do a conditional modifier
- [Tutorial]: recommend onclick and other native on{event} properties instead of using the on modifier HOT 1
- Remove `@babel/standalone` so we can drop unused plugins HOT 1
- [Tutorial] show using AbortController for simpler cleanup with event listeners HOT 1
- 404 from swapi.dev HOT 1
- For tutorial chapters that use console.log, use the <InViewportLogs /> component from kolay
- Integrate with 3rd party documentation tools (Vitepress, Notebooks, etc)
- Could not find module `limber/helpers/state` imported from `(require)` HOT 1
- Reference: Similar Projects
- tutorial todos HOT 18
- Glimmer tutorial, text editing does not work after first load HOT 5
- Add support for multi-file "projects"
- Allow alternate storage options -- github gists, etc
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from limber.