With the exception of the useResource + class combination, all Thunks are optional.
The main caveat is that if your resources will not update without a thunk -- or consuming
tracked data within setup / initialization (which is done for you with useFunction).
The thunk is "just a function" that allows tracked data to be lazily consumed by the resource.
Note that thunks are awkward when they aren't required -- they may even be awkward
when they are required. Whenever possible, we should rely on auto-tracking, such as
what trackedFunction provides.
So when and why are thunks needed?
when we want to manage reactivity separately from a calling context.
in many cases, the thunk is invoked during setup and update of various Resources,
so that the setup and update evaluations can "entangle" with any tracked properties
accessed within the thunk. This allows changes to those tracked properties to
cause the Resources to (re)update.
when an object is passed where the key named is not present,
this.args.named will contain the result of the thunk and this.args.positional
will be empty.
All subclasses of Resource and LifecycleResource have a static method, with.
This with method takes the same argument Thunk you'll see throughout other usages
of Resources in this document.
The type of data in this example will be an instance of SomeResource, so that
typescript is happy / correct.
characters would be accessed via this.info.value.characters in the StarWarsInfo class
While this example is a bit contrived, hopefully it demonstrates how the state arg
works. During the first invocation, state is falsey, allowing the rest of the
function to execute. The next time this.ids changes, the function will be called
again, except state will be the { characters } value during the first invocation,
and the function will return the initial data.
This particular technique could be used to run any async function safely (as long
as the function doesn't interact with this).
In this example, where the function is async, the "value" of info.value is undefined until the
function completes.
To help prevent accidental async footguns, even if a function is synchronous, it is still ran
asynchronously, therefor, the thunk cannot be avoided.
Accessing the result of Math.random() would be done via:
{{this.rand.value.value}}
Something to note about composing resources is that if arguments passed to the
outer resource change, the inner resources are discarded entirely.
For example, you'll need to manage the inner resource's cache invalidation yourself if you want
the inner resource's behavior to be reactive based on outer arguments:
useResource is what allows Resources to be used in JS, they hide the reactivity APIs
from the consumer so that the surface API is smaller. Though, from an end-user-api
ergonomics perspective, you wouldn't typically want to rely on this. As in
ember-data-resources
the useResource + Resource class are coupled together in to more meaningful APIs --
allowing only a single import in most cases.
import { useResource } from'ember-resources';
classMyClass { data = useResource(this, SomeResource, () => [arglist]); }
When any tracked data in the args thunk is updated, the Resource will be updated as well
The this is to keep track of destruction -- so when MyClass is destroyed, all the resources attached to it can also be destroyed.
The resource will do nothing until it is accessed. Meaning, if you have a template that guards
access to the data, like:
ember-resources does not provide or depend on ember-concurrency.
If you want to use useTask, you'll need to add ember-concurrency as a dependency
in your project.
example
When this.id changes, the task will automatically be re-invoked.
With the exception of the
useResource+classcombination, all Thunks are optional. The main caveat is that if your resources will not update without a thunk -- or consuming tracked data within setup / initialization (which is done for you withuseFunction).Note that thunks are awkward when they aren't required -- they may even be awkward when they are required. Whenever possible, we should rely on auto-tracking, such as what trackedFunction provides.
So when and why are thunks needed?
The args thunk accepts the following data shapes:
An array
when an array is passed, inside the Resource,
this.args.namedwill be empty andthis.args.positionalwill contain the result of the thunk.for function resources, this is the only type of thunk allowed.
An object of named args
when an object is passed where the key
namedis not present,this.args.namedwill contain the result of the thunk andthis.args.positionalwill be empty.An object containing both named args and positional args
when an object is passed containing either keys:
namedorpositional:this.args.namedwill be the value of the result of the thunk'snamedpropertythis.args.positionalwill be the value of the result of the thunk'spositionalpropertyThis is the same shape of args used throughout Ember's Helpers, Modifiers, etc