Why use OnPush in Angular? Not for performance...

Поделиться
HTML-код
  • Опубликовано: 5 сен 2024

Комментарии • 54

  • @JoshuaMorony
    @JoshuaMorony  Год назад

    Join my mailing list for more exclusive content and access to the archive of my private tips of the week: mobirony.ck.page/4a331b9076

  • @phugia963
    @phugia963 Год назад +35

    first off I think it's great to have deep knowledge about Angular stuffs (change detection, rxjs) & messing with them. But after refactoring, you ended up with 2 nested pipes and a HOO (Higher Order Observable) inside another HOO, which definitely harder to debug & understand. Async pipe might be great but if it make your code harder to read & trace, just go with the normal way, subscribe & assign value to a variable, bind that value to template & let Angular take its course.

  • @maximlyakhov967
    @maximlyakhov967 Год назад +14

    I would create BehaviorSubject and push new values within the Input setter and a subscription to subject with delay to project the data to template. This would be easier to test and the data is split from reactive mapping without introducing flattening operators.

  • @walboey
    @walboey 2 года назад +5

    I'm back into working w/ angular and searched a topic and now your all videos are being recommended to me. I am not disappointed; your videos are excellent!

  • @Blafasel3
    @Blafasel3 Год назад +8

    Nice video! I switched over to using onPush for every component for the reasons you mentioned. Another reason I see which is not explicitely mentioned: Once you understand when the changeDetection is triggered when set onPush, it becomes very clear and concise (and easy to test) changes in the component because there is only a very limited number of events if your component is as small as it should be. There is also no way for someone else to mess changeDetection up - I've seen horrible inplace mutation of arrays and objects inside that array which only worked because of the default Angular ChangeDetection setting. I honestly believe there should only be OnPush or it should be the default at least. Even for small apps. Performance gain is nice 2 have and makes sense, but as you said, it just makes you write much clearer code which is way easier to understand if you just take the time to watch your explanation ;)

  • @davesuico9950
    @davesuico9950 2 года назад +6

    Thank you for your efforts! I've finally found a better angular channel to learn from!

  • @Jul05
    @Jul05 Год назад +3

    Thank you so much! All of your videos are amazing! You are the best source for advanced Angular topics by far.

  • @JoboyJordan
    @JoboyJordan Год назад +1

    Hi Joshua, thanks for this great video 👍 It gave me clear understanding on the benefits of creating components reactively (also functional via pipe()). I'm the one who asked ealier regarding the benefits of why use RxJS over standard way. I think this video answered that. THANKS!

  • @omergronich778
    @omergronich778 2 года назад +11

    Correction- the async pipe does not trigger change detection, it calls markForCheck that tells angular to check the component in the next change detection cycle

    • @ytamb01
      @ytamb01 2 года назад +1

      That sounds like a subtle, but important, point. What's the difference? When would the next change detection cycle be?

    • @omergronich778
      @omergronich778 2 года назад +11

      ​ @Andy Brewer
      Angular uses zone.js to trigger change detection.
      So the following things can trigger change detection in angular:
      1. all things zone.js patches (Web apis, browser events, ajax requests etc...)
      2. manually calling detectChanges() which tells Angular to run change detection on the component and it's children
      3. calling ApplicationRef.tick() which tells Angular to run change detection for the whole application.
      By default angular will check every single component in the component tree for changes unless that component's change detection strategy is set to OnPush, in that case angular decides whether to skip that component or not. The way it determines whether to skip it is by checking the following things:
      1. If an Input reference has changed
      2. An event originated from the component or one of its children.
      3. The component was marked for check (like what the async pipe does)

    • @omergronich778
      @omergronich778 2 года назад +1

      ​ @Andy Brewer
      hope that clears it up

    • @JoshuaMorony
      @JoshuaMorony  2 года назад +12

      I am aware of this so hopefully the video isn't misleading, I mentioned that the async pipe also uses the CDR to trigger change detection - which it does by using markForCheck() and then a tick() is triggered, which is going to cause the component to be updated just like detectChanges() would do if we did that manually ourselves. Maybe the video could have been clearer here, but it's also a deeper point that I didn't really want to get bogged down on. Thanks for the comment!

    • @konstantinkim4148
      @konstantinkim4148 Год назад

      then why the example works with all the photoes? With markForCheck changeDetection only works on the next cycle, but here we got all the photoes displayed instead of skipping the last one

  • @ravindradevrani
    @ravindradevrani Год назад

    What a clean explanation. Great content as always.

  • @tristanmorris5646
    @tristanmorris5646 4 месяца назад

    Some comments are taking this video as evidence that rxjs is bad (at least for this situation). But there's always multiple ways to do something, and maybe people might find something like this more intuitive:
    this.currentPhoto$ = zip(interval(500), from(value.reverse())).pipe(
    map(([intervalIndex, photo]) => photo)
    }
    Learning zip, interval, and from can be tricky, but the hardest one here is zip... and playing with the rxjs marbles diagram for it gives an intuitive understanding super quickly.

  • @JohanCoppieters
    @JohanCoppieters 2 года назад +7

    I must say the onPush: I’m totally pro. Makes you think about your code.
    As a lecturer, developer and dev mgr, I often see functional code which is very readable at writing time and while explaining. However for other developers reading your code and even for yourself 2 years later, it’s hard see what it does exactly.
    The number of lines doesn’t matter, readability to other people is much more important.

    • @frankseverijnen859
      @frankseverijnen859 2 года назад +4

      Second this. Readable code is in most cases way more important than the most technically beautiful code.

  • @jordisarrato331
    @jordisarrato331 2 года назад

    I use change detection in my website and this is very interesting!

  • @julienwickramatunga7338
    @julienwickramatunga7338 2 года назад

    Really nice example, thank you very much!

  • @xxRAP13Rxx
    @xxRAP13Rxx Месяц назад

    Thanks for the video! At 6:50, onPush change detection strategy won't enable change detection because `currentPhoto` isn't marked with @Input() ?

  • @bronzekoala9141
    @bronzekoala9141 Год назад

    Nice, however I shiver at the thought of unittesting stuff like this.

  • @pippaloves
    @pippaloves 9 месяцев назад

    Thank your for this video

  • @RoiTrigerman
    @RoiTrigerman 2 года назад +1

    Thanks for the great videos :) Regarding the photos example, I see many people saying that you shouldn't pass an observable to an input. Do you disagree?

    • @JoshuaMorony
      @JoshuaMorony  2 года назад +2

      I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too.

  • @Almighty_Flat_Earth
    @Almighty_Flat_Earth 2 года назад +1

    Would have been better if you had provided stackblitz example.

  • @dale_nguyen
    @dale_nguyen 2 года назад

    Thanks for the video 👍

  • @TheSaceone
    @TheSaceone 2 года назад

    thank you for the video! it's gonna be useful for a particular feature I'm working on in an app of mine! Btw why did you reverse your photo array?

    • @JoshuaMorony
      @JoshuaMorony  2 года назад

      Just because I wanted the photos to display in reverse order (last should be shown first) - nothing special going on there!

  • @samucancld
    @samucancld Год назад

    Great video 👍

  • @IamPali2024
    @IamPali2024 4 месяца назад

    This example shows why exactly you shouldn’t use onPush. And if there are any performance gains, it should be Angular who come up with performance updates for their product not developers

  • @WanderingCrow
    @WanderingCrow 2 года назад

    Thanks a lot for this video, this is actually something I've started to do recently, so I'm glad to see the long-term benefits it can bring. Also, I must confess being a little disappointed by your newsletter, that enforce the use of a .subscribe(), instead of your beloved async pipe 🤔😏

  • @LeungWong
    @LeungWong 2 года назад

    What’s the approach to detect change in dynamic created components? You would need to use ChangeDetectorRef right?

  • @CeezGeez
    @CeezGeez Год назад

    ty

  • @stanisawgolinski3244
    @stanisawgolinski3244 2 года назад +1

    Isn't pushing Observables through Input an antipattern?

    • @JoshuaMorony
      @JoshuaMorony  2 года назад

      I responded to a similar question in another comment: "I generally agree, most of the time I will use the async pipe to subscribe to the stream and pass synchronous values to the component as an input. Important not to be too dogmatic about these things though, sometimes breaking the "rules" can be beneficial or useful. One argument for passing in an observable as an input is that the input reference won't change so there is going to be less change detection triggered with OnPush - so potential significant performance gains there under the right circumstances. Maybe sometimes it just makes the code easier/cleaner and that's fine too."

  • @noss403
    @noss403 2 года назад

    And how to stop/interrupt this slideshow?

  • @bobar92
    @bobar92 2 года назад

    Thanks for the video! Is there a way to set OnPush globally, instead for each component, or would you advise against doing it for every component?

    • @GLawSomnia
      @GLawSomnia 2 года назад +2

      You can set it in angular.json, but it will only affect the components that you will create in the future (with the CLI)

    • @JoshuaMorony
      @JoshuaMorony  2 года назад +2

      What GLaw said, also you can supply flags when generating components if you want, and I would not advise against it - I think you should do it for every component :) having some OnPush and some not would probably just make things confusing.

    • @bobar92
      @bobar92 2 года назад +1

      @@GLawSomnia Thank you for the answer!

    • @bobar92
      @bobar92 2 года назад +1

      @@JoshuaMorony Thanks for the answer, I think I'll do that from now on 😁

  • @rajatnegi3896
    @rajatnegi3896 2 года назад +2

    I like your accent man

  • @The14Some1
    @The14Some1 10 месяцев назад

    11:25 ain't this violate the "reactive" coding rule? You must have subscribed somewhere to get the list of photos as an array.

  • @aqliyusoff8355
    @aqliyusoff8355 2 года назад

    get a helping answer thanks.....

  • @lucasgiunta8874
    @lucasgiunta8874 2 года назад

    But it is not a good practice to give a stream to a component.

    • @JSCHM
      @JSCHM Год назад +1

      In the last version of the code no observables are passed between components. Instead, the setter converts the value to an observable (and the currentPhoto$ observable is "converted" back to a normal value via the `async`-pipe).

  • @ItsBlew
    @ItsBlew 2 года назад

    Sae

  • @tudorcranfield2183
    @tudorcranfield2183 2 года назад

    😂 Pᵣₒmₒˢᵐ

  • @komodoutd
    @komodoutd 2 года назад +1

    Couldn't you have totally skipped concatMap here and just go with this:
    from(value.reverse()).pipe(delay(500)) ?

    • @JoshuaMorony
      @JoshuaMorony  2 года назад +4

      The delay operator will add a delay before the stream starts emitting, but it won't add a delay for *each* emission. That's why the concatMap is required here, because I want to wait 500ms between each emission, so I have to create an individual stream for each individual photo so that the delay will work how I want.

    • @komodoutd
      @komodoutd 2 года назад

      @@JoshuaMorony Yes, now it makes sense. thanks a lot for the detailed explanation!