-
Notifications
You must be signed in to change notification settings - Fork 26.3k
feat(core): introduce afterRenderEffect
#57549
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Deployed adev-preview for 59e169c to: https://ng-dev-previews-fw--pr-angular-angular-57549-adev-prev-oyzoua3t.web.app Note: As new commits are pushed to this pull request, this link is updated after the preview is rebuilt. |
I love this change, but since the signalification of the primitive values returned by each phase callback is abstracted away from us, this seems to be a bad fit for use cases that require multiple values to be passed from one phase to another. For example: afterRenderEffect({
earlyRead: () => {
const element = this.someElementRef.nativeElement;
return [element.offsetLeft, element.offsetTop];
},
write: ([x, y]) => animateThing(x, y)
}) In this example, I assume that the write phase would run on every render because the earlyRead phase returns a new array each time. Is that correct? The workaround I use today isn't very ergonomic: position = signal<[number,number]>([0,0], {equal: arrayDeepEqual});
afterRender({
earlyRead: () => {
const element = this.someElementRef().nativeElement;
this.position.set([element.offsetLeft, element.offsetTop]);
}
});
// only run the write phase when this.position changes, after accounting for custom equality fn
effect(() => {
const [x, y] = this.position();
afterNextRender({ write: () => animateThing(x, y) });
}); It seems to work, but it feels brittle. What do you think? |
} | ||
} finally { | ||
// Even if a cleanup function errors, ensure it's cleared. | ||
this.cleanup?.clear(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this mean that if one cleanup function errors, the others don't run but are still cleared?
Should this be more like
for ... {
try {
cleanupFn();
} finally {
this.cleanup?.delete(cleanupFn);
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about this before, and chose (for no strong reason other than intuition) to implement a mental model where we considered all the registered cleanup functions to be a part of the same logical operation. We really need to standardize this model (effect()
for example overwrites the previous cleanup function when a new one is registered).
LGTM overall but adding a cleanup label since:
|
4c72cab
to
4f34cef
Compare
Yeah, we saw this too, but I don't know if there is an API that's ideal here. There seem to be two potential design avenues which address the problem:
Both of these solutions have their downsides. With equality functions, you have to remember to pass them. With more granular return objects, the signal wrapping only works at one layer - if I get creative and |
4f34cef
to
4eee890
Compare
Implement the `afterRenderEffect` primitive, which creates effect(s) that run as part of Angular's `afterRender` sequence. `afterRenderEffect` is a useful primitive for expressing DOM operations in a declarative, reactive way. The API itself mirrors `afterRender` and `afterNextRender` with one big difference: values are propagated from phase to phase as signals instead of as plain values. As a result, later phases may not need to execute if the values returned by earlier phases do not change.
4eee890
to
59e169c
Compare
@alxhub the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reviewed-for: public-api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed-for: public-api
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewed-for: size-tracking
This PR was merged into the repository by commit be2e496. The changes were merged into the following branches: main |
This issue has been automatically locked due to inactivity. Read more about our automatic conversation locking policy. This action has been performed automatically by a bot. |
Implement the
afterRenderEffect
primitive, which creates effect(s) that run as part of Angular'safterRender
sequence.afterRenderEffect
is a useful primitive for expressing DOM operations in a declarative, reactive way.The API itself mirrors
afterRender
andafterNextRender
with one big difference: values are propagated from phase to phase as signals instead of as plain values. As a result, later phases may not need to execute if the values returned by earlier phases do not change.