emberjs / emberjs/rfcs

Deprecate `recompute` on `Helper`?

Open
#633 9 comments 1 reaction 0 assignees View on GitHub
Seeking Co-author
Dominant language
No language data
Stars
801
Forks
409
PR merge metrics
No merged PRs in 30d

Description

The point of the `recompute` method is that you call it to trigger Ember to re-render the helper in some kind of callback, such as observers, promise callbacks, etc.

Since helpers are now auto-tracked, it should be possible to refactor any usage of `recompute` into setting tracked state and accomplish the same thing. In most cases, the transformation is pretty straightforward.

Before:

```js
class MyHelper extends Helper {
@service foo;

@observes('foo.bar.[]') onBarChanged() {
this.recompute();
}

compute() {
return this.foo.bar.map(...);
}
}
```

After:

```js
class MyHelper extends Helper {
@service foo;

@tracked bar = this.foo.bar;

@observes('foo.bar.[]') onBarChanged() {
this.bar = this.foo.bar;
}

compute() {
return this.bar.map(...);
}
}
```

The example uses an observer, because that is a common kind of callback used in this position, but it's ultimately unrelated to my point.I also think there are better ways to accomplish the same thing without using observers, in general, but that is also besides the point here.

For prosperity, here is a second example that doesn't use observers:

Before:

```js
const UNINITIALIZED = Object.freeze({});

class AwaitHelper extends Helper {
resolved = undefined;
promise = UNINITIALIZED;

compute([promise]) {
if (promise !== this.promise) {
this.resolved = undefined;
this.promise = promise;

Promise.resolve(promise).then(resolved => {
if (promise === this.promise) {
this.resolved = resolved;
this.recompute();
}
});
}

return this.resolved;
}

willDestroy() {
this.promise = UNINITIALIZED;
}
}
```

After:

```js
const UNINITIALIZED = Object.freeze({});

class AwaitHelper extends Helper {
@tracked resolved = undefined;
promise = UNINITIALIZED;

compute([promise]) {
if (promise !== this.promise) {
this.resolved = undefined;
this.promise = promise;

Promise.resolve(promise).then(resolved => {
if (promise === this.promise) {
this.resolved = resolved;
}
});
}

return this.resolved;
}

willDestroy() {
this.promise = UNINITIALIZED;
}
}
```

The general pattern is: instead of calling `recompute()`, just refactor into setting a piece of tracked state and use that tracked state in the `compute()` method.

Would be interested to see if there are any valid use cases this doesn't cover.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.