Thanks to visit codestin.com
Credit goes to github.com

Skip to content

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

Closed
wants to merge 1 commit into from

Conversation

alxhub
Copy link
Member

@alxhub alxhub commented Aug 27, 2024

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.

@angular-robot angular-robot bot added detected: feature PR contains a feature commit area: core Issues related to the framework runtime labels Aug 27, 2024
@ngbot ngbot bot added this to the Backlog milestone Aug 27, 2024
Copy link

github-actions bot commented Aug 27, 2024

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.

@garrettld
Copy link
Contributor

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();
Copy link
Contributor

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);
 }
}

Copy link
Member Author

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).

@pkozlowski-opensource pkozlowski-opensource added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Sep 3, 2024
@pkozlowski-opensource
Copy link
Member

LGTM overall but adding a cleanup label since:

  • is missing public API export
  • it has pending comments from other reviewers

@alxhub alxhub force-pushed the prototype/after-render-effect branch from 4c72cab to 4f34cef Compare September 3, 2024 16:27
@alxhub
Copy link
Member Author

alxhub commented Sep 3, 2024

@garrettld

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.

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:

  1. Allowing some configuration of equality function (which users would have to remember to do). This could be specified in the options, or by allowing the signal to be directly created by the user somehow.

  2. Increasing the granularity at which we make signals out of the returned object. For example, we could require that phases return an object & make signals out of each property, or explicitly support returning tuples.

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 return {bounds: {x: 0, y: 3}} then I break things again. I think we're leaning in favor of equality as a more explicit solution.

@alxhub alxhub added action: merge The PR is ready for merge by the caretaker and removed action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews labels Sep 3, 2024
@alxhub alxhub force-pushed the prototype/after-render-effect branch from 4f34cef to 4eee890 Compare September 3, 2024 16:53
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.
@alxhub alxhub force-pushed the prototype/after-render-effect branch from 4eee890 to 59e169c Compare September 3, 2024 17:08
@AndrewKushnir AndrewKushnir added the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Sep 3, 2024
@AndrewKushnir
Copy link
Contributor

@alxhub the test CI job is failing with the payload size issue. Could you please update golden file(s)?

@alxhub alxhub added the target: major This PR is targeted for the next major release label Sep 3, 2024
Copy link
Contributor

@thePunderWoman thePunderWoman left a 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

Copy link
Contributor

@AndrewKushnir AndrewKushnir left a 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

@AndrewKushnir AndrewKushnir removed the action: cleanup The PR is in need of cleanup, either due to needing a rebase or in response to comments from reviews label Sep 3, 2024
Copy link
Contributor

@AndrewKushnir AndrewKushnir left a 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

@AndrewKushnir
Copy link
Contributor

This PR was merged into the repository by commit be2e496.

The changes were merged into the following branches: main

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 4, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker adev: preview area: core Issues related to the framework runtime detected: feature PR contains a feature commit target: major This PR is targeted for the next major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants