User-land template-escape into JS
- Dominant language
- No language data
- Stars
- 801
- Forks
- 409
- PR merge metrics
- No merged PRs in 30d
Description
Opening this issue to track ideas and exploration, and not so much to debate _if we should or not_ (which is tiring, and we can talk about it on discord or in person/video call).
With [First-Class Component templates](https://github.com/emberjs/rfcs/pull/779) merged, we can use the `` syntax as a way of discussing strict-mode transforms in a concise, human-readable format.
However, this RFC Issue assumes we have the following available in our hypothetical `ember-source` version:
- [Default Helper Manager](https://github.com/emberjs/rfcs/pull/756)
- Allows plain functions to be used as helpers, with nothing "embery" about them
- _Implementation in-progress_
- Polyfill: https://github.com/NullVoxPopuli/ember-functions-as-helper-polyfill
- [Default Modifier Manager](https://github.com/emberjs/rfcs/pull/757)
- Allows plain functions to be used as modifiers, with nothing "embery" about them
- _Currently unmerged RFC_
- Polyfill: https://github.com/NullVoxPopuli/ember-functions-as-modifiers-polyfill/
This may accidentally come off as RFC-ish, but I just want to get my thoughts down real quick after having a chat with @jimschofield about some directions we _can_ go in. It's def too early to submit an actual RFC on this idea -- need to do some exploration.
Presently, double curlies are defined as an escape to glimmer-S-Expressions, everything else is text or html -- which leaves a lot of room open to explore additional syntaxes _while evaluating the **utility** of a concept_.
For example, today in Ember, we don't have any concept of "Effects", yet they are highly needed in one of the bigger concepts missing from the switch to Octane (people formally used observers for this type of behavior).
_In userland, as an addon author_, it would be possible to transform this:
```jsx
// some-component.gjs
{() => console.log(@data)}
```
into a valid Ember template _today_:
```jsx
const helperA = (data) => console.log(data);
{{ (helperA @data) }}
```
Similarly, we can make some assumptions within our hypothetical transform to handle `this` in a class-based component, for example:
```jsx
class Demo extends Component {
@tracked searchText = '';
// tho, what about concurrency, cancellation, dropping, etc? lots to figure out with something like this
{async () => {
let { endpoint } = @searchProvider; // destructure named args
let response = await fetch(`${endpoint}?query=${this.searchText}`);
let json = await response.json();
// set the auto-completion
this.searchText = json.suggestions[0];
}}
}
```
would become:
```jsx
import { isDestroying, isDestroyed } from '@ember/destroyable';
const helperA = async (ctx, searchProvider) => {
let { endpoint } = searchProvider; // destructure named args
let response = await fetch(`${endpoint}?query=${ctx.searchText}`);
let json = await response.json();
// we can automatically protect people from destruction blunders
if (isDestroying(ctx) || isDestroyed(ctx)) return;
// set the auto-completion
ctx.searchText = json.suggestions[0];
}
class Demo extends Component {
@tracked searchText = '';
{{helperA this @searchProvider}}
}
```
First off, why bother? This looks pretty close to JSX and implies a bunch of things which we might open ourselves up to if we aren't careful.
But the motivation is that isn't a good way to implement "Effects" on a JS class.
Some options we have:
```js
class MyComponent {
@effect nameWasted = () => console.log(this.args.data);
@effect(() => [this.args.data]
nameNotNeeded(passedArg) {
console.log(passedArg);
}
}
```
Goals of an effect:
- auto-track (consume tracked data before an `await`)
- run initially during render and _only_ when tracked data updates
Problems with effects-in-classes
- a name is needed
- no good way to create a syntax?
What do effects look like in React? they get around the whole problem by:
- only using functions, so naming the effect is not a concern
- default to infinitely rendering (somewhat solved by linting (linting required to not make obvious mistakes (You may scoff at this, but I think ember is at a point where we should be doing this, and already are in many places)))
- examples
- https://github.com/ember-cli/eslint-plugin-ember/issues/1413
- https://github.com/ember-cli/eslint-plugin-ember/pull/1421
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/avoid-leaking-state-in-ember-objects.md
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-attrs-in-components.md
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-controllers.md
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-get.md
- https://github.com/ember-cli/eslint-plugin-ember/blob/master/docs/rules/no-proxies.md
- and many more!
```jsx
function Demo() {
let [data] = useState();
// runs every time data chages, due to the dependency list in the second argument
useEffect(() => {
console.log(data);
}, [data])
return <>;
}
```
Some benefits of exploring this:
- destructuring of named arguments
- implementation of "effects" without actually implementing effects (these are just helpers)
- save people from interacting with a `this` object after an `await`
- we can make this an auto-fixable eslint rule in existing code: https://github.com/ember-cli/eslint-plugin-ember/pull/1421
Outstanding things to figure out:
- usage with let?
- using return values?
- how to _manage_ async state (since _The Platform doesn't do this for us)
- concurrency?
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.