Latest updates to effect() in Angular


Authors: Alex Rickabaugh Mark ThompsonAngular’s developer preview process enables us to give developers a chance to try out APIs and features before they are promoted to production, and allows us the flexibility to adjust those APIs in response to real-world usage and feedback. Through this feedback on the effect() API during its developer preview, we’ve identified several improvements to the design that will address real issues with both functionality and developer experience. As a result, we’re making meaningful changes to the API that we are excited to share.Removing allowSignalWritesOccasionally it’s necessary to set signals within an effect(), but more often it’s a sign that effects are the wrong tool for the job, and another API like computed() would be a better fit. To encourage good patterns, our initial design for the effect() API prohibited setting signals unless the allowSignalWrites flag was explicitly set.Through developer feedback and observing real-world usage, we’ve concluded that the flag is not providing enough value. We’ve found that it wasn’t effective at encouraging good patterns, and ended up discouraging usage of effect() in cases where it would be reasonable to update signals.As a result, we’ve decided to remove the allowSignalWrites flag entirely in v19, and allow effects to set signals by default. Instead, we’ll focus on other ways of encouraging good patterns, including the addition of new reactivity helpers where needed.Timing is importantWe’re also making a significant change to the timing of when effects run. Specifically, we’re moving effects to run as a part of the component hierarchy during change detection, whereas today they’re queued and scheduled independently as microtasks.The goals of this change is to make effect timing more predictable and useful, resolve a number of bugs around effects running too early or too late, and ensure that “parent” effects run before “child” effects relative to the component tree.How does this impact your projects?In general, most uses of the effect API will continue to work as expected but here are a few use cases to be mindful of:

  • Effects against view query results, which relied on running after change detection in order to read DOM properties such as .offsetWidth. The new afterRenderEffect API is usually a drop-in replacement.
  • toObservable() of input signals now emit earlier than before, which affects the timing of Observable chains. In some cases, we’ve seen this make unnecessary additional RPCs.
  • Relying on the availability of non-signal view queries inside of effects (signal queries can help here, as can afterRenderEffect).
  • Interaction with forms Observables and values are particularly sensitive to timing issues — often creating the effect late or using a signal to delay the effect until ngAfterViewInit or afterNextRender can help.

When testing this change at Google, we fixed around 100 cases where the timing change meaningfully impacted code. Around half of these were test-only changes, and in a few cases the timing difference led to more correct application behavior.Will effects still be in preview?Yes. As a result of these changes, we’ve decided to keep effect() in developer preview through at least v19.0. Assuming the feedback around the new behavior is positive, we plan to stabilize the API in an upcoming minor.Feedback is a giftThank you to all of you, the Angular community, for the important impactful role you play in the development of Angular. We were able to arrive at this moment and many like it due to your continued support and enthusiasm for this framework. This change is landing soon and you can test it in the latest RC and we hope you enjoy it.Latest updates to effect() in Angular was originally published in Angular Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.