-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1035 from NullVoxPopuli/more-docs
More docs
- Loading branch information
Showing
3 changed files
with
458 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# What are "lifetimes"? | ||
|
||
Sometimes known as [Object lifetime](https://en.wikipedia.org/wiki/Object_lifetime) is the timespan between creation and destruction of an object. | ||
|
||
Resources have two possible lifetimes: | ||
- Resources' lifetime is _linked_ to their parent context | ||
- this is typically in JavaScript, on a component, service, or custom class | ||
- Resources' lifetime is contained within the block in which they are rendered | ||
- this is typically in the template | ||
|
||
## When is a resource created? | ||
|
||
In JavaScript, a resource is created only upon access. Like services, there is no runtime cost to the definition of the property that you'll eventually use. Only when accessed does something happen (that something being the initial invocation of the resource). | ||
|
||
In templates, a resource is created / initially invoked when rendered. | ||
|
||
## When is a resource destroyed? | ||
|
||
In JavaScript, a resource is destroyed when the parent / containing object is destroyed. This could be when a component is no longer needed, or when a service is destroyed, such as what would happen at the end of a test. | ||
|
||
In templates, a resource is destroyed when it is no longer rendered. | ||
|
||
```hbs | ||
{{#if condition}} | ||
{{LocalizedClock 'en-US'}} | ||
{{/if}} | ||
``` | ||
|
||
In this example, the `LocalizedClock` will be created when `condition` is true, and then destroyed when `condition` is false. | ||
|
||
|
||
When a resource is destroyed, its `on.cleanup()` (set of) function(s) runs. | ||
|
||
|
||
### When arguments change | ||
|
||
When the argument-reference changes, the resource will be destroyed, and will be re-created. | ||
|
||
For example: | ||
|
||
```js | ||
import Component from '@glimmer/component'; | ||
import { tracked } from '@glimmer/tracking'; | ||
import { on } from '@ember/modifier'; | ||
|
||
import { LocalizedClock } from './clock'; | ||
|
||
export default Demo extends Component { | ||
<template> | ||
{{LocalizedClock this.locale}} | ||
|
||
<button {{on 'click' this.changeLocale}}>Update</button> | ||
</template> | ||
|
||
@tracked locale = 'en-US'; | ||
|
||
changeLocale = (newLocal) => this.locale = newLocal; | ||
} | ||
``` | ||
|
||
Once `this.locale` changes, `LocalizedClock` will be destroyed, and is created again with the new `this.locale` value. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# What is `resourceFactory`? | ||
|
||
## Why do we need it? | ||
|
||
`resourceFactory` provides a compatibility with the design goal of resources given the current limitations of public APIs in Ember. The idea is that we'd eventually get rid of it, and even right now, it's basically a no-op. | ||
|
||
<details><summary>What will removing resourceFactory look like?</summary> | ||
|
||
This will be automated via codemod in the future, so its best to not worry about the details here, but for the curious: | ||
|
||
Since `resourceFactory` is almost no-op function, you would delete it, like this: | ||
```diff | ||
function LocalizedClock(locale) { | ||
return resource(() => { | ||
/* ... */ | ||
return 'theValue'; | ||
}); | ||
} | ||
|
||
- resourceFactory(LocalizedClock); | ||
``` | ||
|
||
Or if you have code that more aggressively wrapped the function with `resourceFactory`, your change would look like this: | ||
|
||
```diff | ||
- const LocalizedClock = resourceFactory((locale) => { | ||
+ function LocalizedClock(locale) { | ||
return resource(() => { | ||
/* ... */ | ||
return 'theValue'; | ||
}); | ||
- }); | ||
+ } | ||
``` | ||
|
||
|
||
</details> | ||
|
||
The behavior that we _want_ when we define a resource like this: | ||
```js | ||
function LocalizedClock(locale) { | ||
return resource(() => { | ||
/* ... */ | ||
return 'theValue'; | ||
}); | ||
} | ||
``` | ||
and invoke it like this: | ||
```hbs | ||
{{LocalizedClock 'en-US'}} | ||
``` | ||
|
||
We want, not only the function, `LocalizedClock` to be invoked, but also the `resource`. | ||
|
||
One way to think about this is to strip away all the resource-isms and you're left with: | ||
```js | ||
function localizedClock(locale) { | ||
return function myInnerFunction() { | ||
return 'theValue'; | ||
} | ||
} | ||
``` | ||
this would still be invoked via: | ||
```hbs | ||
{{localizedClock 'en-US'}} | ||
``` | ||
but we can more easily see that the return value is a function. | ||
|
||
So, like with `LocalizedClock`, we have to do this for `localizedClock`: | ||
```hbs | ||
{{#let (localizedClock 'en-US') as |innerFunction|}} | ||
{{ (innerFunction) }} -- prints theValue | ||
{{/let}} | ||
{{#let (LocalizedClock 'en-US') as |theResource|}} | ||
{{ (theResource) }} -- prints theValue | ||
{{/let}} | ||
``` | ||
|
||
We don't want to have to use `let` for each time we want to use a resource, so that's what `resourceFactory` helps out with. | ||
|
||
`resourceFactory` _immediately_ invokes that returned function. | ||
It's a side-effecting API, so it can be used like this: | ||
|
||
```js | ||
function LocalizedClock(locale) { | ||
return resource(() => { | ||
/* ... */ | ||
return 'theValue'; | ||
}); | ||
} | ||
|
||
resourceFactory(LocalizedClock); | ||
``` | ||
|
||
which then allows for: | ||
```hbs | ||
{{LocalizedClock 'en-US'}} -- prints theValue | ||
``` | ||
|
||
|
||
## What is it it? | ||
|
||
This is the [implementation](https://github.com/NullVoxPopuli/ember-resources/blob/2608052dcb740223ad83aef679f4406328894669/ember-resources/src/core/function-based/immediate-invocation.ts#L132): | ||
```js | ||
export function resourceFactory(wrapperFn) { | ||
setHelperManager(ResourceInvokerFactory, wrapperFn); | ||
|
||
return wrapperFn; | ||
} | ||
``` | ||
|
||
It's _almost_ an "identity function", i.e: `x => x`. | ||
|
||
Docs: | ||
|
||
- [`setHelperManager`](https://api.emberjs.com/ember/release/functions/@ember%2Fhelper/setHelperManager) | ||
|
||
## Can I _not_ use it? | ||
|
||
Yes, if you are reasonbly certain that your Resource doesn't need to directly be invoked in templates | ||
|
||
|
||
For example: | ||
|
||
```js | ||
import Component from '@glimmer/component'; | ||
import { use, resource } from 'ember-resources'; | ||
|
||
function LocalizedClock(locale) { | ||
return resource(() => { | ||
/* ... */ | ||
return 'theValue'; | ||
}); | ||
} | ||
|
||
export default class Demo extends Component { | ||
@use data = LocalizedClock('en-US'); | ||
|
||
<template> | ||
{{this.data}} -- prints 'theValue' | ||
</template> | ||
} | ||
|
||
``` | ||
|
||
Calling `LocalizedClock` is _just a function call_ -- in JavaScript, there is nothing special between "resources with arguments" vs "functions that return functions", as they could be thought of as the same. | ||
|
||
However, in JavaScript, and like with all reactive systems, you must manage reactivity yourself. In the template, we're used to this being automatic, but in JavaScript, if you want reactive arguments to your resource, you'll need to use a technique to _defer_ evaluating the value, this could be an arrow-function, object of getters, or a config object/class-instance. This is a topic for another time tho, on general-reactivity. | ||
|
||
|
Oops, something went wrong.