Don't Use Effects 🚫 and What To Do Instead 🌟 w/ Alex Rickabaugh, Angular Team

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

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

  • @bruceweng3895
    @bruceweng3895 2 месяца назад +13

    This is a simple and elegant explanation on solving double source of truth problem. This will be extremely helpful to document the pattern in Angular official documentation show case what a better alternative to effect.❤

  • @JaiKrsh
    @JaiKrsh 3 месяца назад +25

    12:54 I understand the approach, but my brain immediately panicked thinking about `{{ myName()() }}`

    • @MaratMartirosyan-qx5qx
      @MaratMartirosyan-qx5qx 3 месяца назад +1

      Agree, it is not convenient.

    • @techstacknation
      @techstacknation  2 месяца назад +3

      We're all wrapping our heads around this, posting more this week from our follow up discussion with Ben Lesh. Feel free to come join our live calls, we're all learning this stuff together. 🥰

    • @RobinDaubAraCom
      @RobinDaubAraCom 2 месяца назад

      @@techstacknation Is the video for that talk with Ben Lesh already out? Since you said this week, and Im watching this 8 days later, but cant see a new video on this channel.

    • @techstacknation
      @techstacknation  2 месяца назад +1

      Hi Robin, here you go! 🤗 ruclips.net/video/sQ96BREhAJg/видео.html
      You can see videos like this sooner if you join our free community at TechStackNation.com and if you join our premium spaces, you can participate in our live calls! 🌟

    • @maratgumerov3523
      @maratgumerov3523 8 дней назад

      myName = computed(()=> ({is: signal(this.name()});
      `Hi! {{myName().is()}}`

  • @MahmutGundogdu
    @MahmutGundogdu 3 месяца назад +45

    “If most users are using a tool incorrectly, it might indicate a flaw in its design.” Think about it. Maybe Angular team need mind-shift.

    • @xucongzhan9151
      @xucongzhan9151 3 месяца назад +1

      It's mostly poor naming... Call it "watcher" and the React "instinct" will be suppressed, and more Vue folks will chime in and promote the right thing todo.

    • @kylerjohnson988
      @kylerjohnson988 3 месяца назад +8

      You’re right that this is important to consider. But consider this: it could be a paradigm shift that brings positive change. In the early days of component based frameworks, people were breaking things up too granularity and others had the opposite problem with giant 5k line components. I remember people saying then, “if people aren’t doing it right then maybe it’s a problem with the design,” but here we are years later and clearly that was the paradigm shift we needed in the web space. Signal-based state is another one paradigm shift and I think it’s absolutely a necessary one. The dependency graph and change detection optimization are more than worth it. It will take time for best practices to develop and for everyone to wrap their heads around it (just like when we shifted from templating engines to component-based frameworks). That doesn’t mean we shouldn’t pursue it.

    • @majorhumbert676
      @majorhumbert676 3 месяца назад

      ​@@xucongzhan9151 effect is the technical term. React didn't invent it. Learn your terminology.
      Vue calls it watcher to be edgy and it causes great confusion. Few developers seem to know that it's intended for effects.

    • @ryanngalea
      @ryanngalea 3 месяца назад +3

      It doesn't help that the Angular team marketed it originally as a great way to listen to changes, but now the narrative has changed.

    • @kylerjohnson988
      @kylerjohnson988 3 месяца назад +2

      @@ryanngalea to my knowledge they’ve never said it was a great way to listen to changes and I know some of them personally. From the beginning, even in the RFC discussions and the live coding sessions with an angular team member, they’ve always said you should use it seldomly. They even have that in the official docs where they talk about use cases for it.

  • @qyihamba7034
    @qyihamba7034 2 месяца назад +6

    While this approach works, it seems more like a workaround. Because overall what we are doing is finding a way to set a computed which is not the intended usage.
    Best scenerio would be that computed are made settable, then we avoid a workaround and things like `signal()()`
    All these point to a tool that requires we workaround its rules of usage for it to cater to our basic needs

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

      Yep, I agree. The terminology alone is confusing. We're talking about something that is meant to be "computed"(), e.g. it's calculated, based on other inputs, but here it's not always computed, because we're sometimes setting parts of it manually. I don't think that'll ever feel right to me :D

    • @HariomPandey-gf3rs
      @HariomPandey-gf3rs 18 дней назад

      @@delie32 This means linkedSignals is the angular teams final solution?

  • @MoTheBlackCat
    @MoTheBlackCat 2 месяца назад +1

    I had the same problem but from the first time Alex started writing 'compu' I immediatly though of signal object used as a state approach. Signals are, to me, amazing and natural while RxJs is still a nightmare for my brain after so long haha! Thanks TSN for the great content and discussions!

    • @techstacknation
      @techstacknation  2 месяца назад +1

      Thanks Mo for the lovely comment! Be sure to say hello if you join our community, would be great to see you in our live calls! ~Bonnie 🥰

  • @valaramchaudhary3216
    @valaramchaudhary3216 Месяц назад +1

    This information was just what I needed!

  • @nathanaelmayne5424
    @nathanaelmayne5424 Месяц назад +2

    I assume the upcoming linkedSignal in v19 will be a better alternative to this approach?

  • @hgoebl
    @hgoebl 12 дней назад +3

    Creating new signals in computed looks really bad to me. I won't do this.

  • @Matrium0
    @Matrium0 2 месяца назад +2

    Not sure how I feel about this. I feel like you could also just have used a setter method for your options-input and set the index signal to -1 THERE. Creating a new signal on every input change does feel a bit weird/hacky to me personally.

  • @cburys
    @cburys 13 дней назад

    I don’t use effects.. but his example problem where he said “ok, but now options is an input that someone is binding into us..” then shows us how people’s solution is to add an effect in the constructor.. IMO that’s over engineering. The simple solution is just a getter and setter.

  • @larshanisch
    @larshanisch 3 месяца назад +10

    Signals in signals is like switchMap in rxjs...

    • @muhamedkarajic
      @muhamedkarajic 3 месяца назад +1

      I thought the same. Literally.

    • @larshanisch
      @larshanisch 3 месяца назад

      @@muhamedkarajic because it's true. 😎

    • @ryanngalea
      @ryanngalea 3 месяца назад +1

      Can you outline the issue with rxjs switchMap?

    • @muhamedkarajic
      @muhamedkarajic 3 месяца назад

      ​@@ryanngalea Its just that we think probably about switchmap when we see observable in observable. Its actually not really 1 to 1 with what they explained in the video.
      What he demonstrated in the video you would not even be able probably to do in RxJS cause you need then to subscribe to a newly created observable.
      The way usually you tackle it is to pipe some observables and put the new state into a subject where you call next. It works in the end similary, you pipe A and produce something and then you throw away everything in B. Its not a side effect in RxJS but it has some other issues. NOW I wonder if those similar issues you get by using this pattern - we will see.
      I think its hard really to discuss it now here in a chat.
      To me really I stay away from signals until probably 24 months pass. They are new, patterns will be discovered and then realised they arent so good. Its a new wheel. In my oppinion they can state "hey its needed for a reason" but you cant replace something like RxJS which was there 20 years by a new "primitive value".

    • @larshanisch
      @larshanisch 3 месяца назад

      @@muhamedkarajic interesting thoughts. I have to play with this kind of idea in rxjs.

  • @UfuUfu-sj3bv
    @UfuUfu-sj3bv 3 месяца назад

    Great stuff and very solid explanation. Definitely a pattern I can see becoming used in the future. Really lovely to see you guys ironing out the kinks with signals in the new way of working with Angular. Would love to see more of such content!

    • @techstacknation
      @techstacknation  2 месяца назад

      If you like the videos, you would love the live calls! Come join us on TechStackNation.com, the live calls are in our premium groups but it's free to join our community and you'll find extra videos there. You seem lovely, hope we see you there! 🤗

  • @a_lodygin
    @a_lodygin 2 месяца назад +5

    I'm not entirely sure, but this approach seems like it could lead to a memory leak in the HTML. With each tick of the computed function, a new signal instance is returned. This means the HTML has to subscribe to each new instance. But what happens to the previous signal? From the framework's perspective, I don't see any mechanism to stop observing the old signals until the component is destroyed. As a result, the number of subscribers might increase with each tick of the computed function, leading to a potential memory leak.
    Please correct me, if I wrong.

    • @joaquinmichelet8343
      @joaquinmichelet8343 Месяц назад +2

      The thing is that those subscriptions are not the same as rxjs subscriptions, in the sense that you have to make sure to unsubsribe from them.
      When the computed runs, the signal previously created inside it is automatically 'disposed', removing it as 'dependecy' of any reactivity depending on it.

  • @tranquility_lane
    @tranquility_lane 2 месяца назад

    Great explanation, thank you!

  • @zzing
    @zzing 2 месяца назад

    If I have a mat-table and a mat-sort I need to use viewChild to get the mat-sort, then assign it to a datasource - effect seems to be the right thing here. Because a data source doesn't seem right to recreate if things change when it has properties to update.

    • @techstacknation
      @techstacknation  2 месяца назад

      Excellent question zzing! If you'd like to create a quick stackblitz and post a link here, I can show it to Alex next time he stops by or, even better, you can join us on TechStackNation.com and ask him yourself! #YouCanSitWithUs 🥰

  • @CodingHaribo
    @CodingHaribo 2 месяца назад +2

    I was shocked by `effect` not running syncronously in the same frame as the signal's set operation. I thought all computeds and effects ran syncronously. Why on Earth is it not?

  • @andreymoskalenko3706
    @andreymoskalenko3706 3 месяца назад

    Very useful, thanks Alex and Bonnie 😊

    • @techstacknation
      @techstacknation  2 месяца назад

      Thank YOU for the appreciation, Andrey 🥰

  • @prasoon2510
    @prasoon2510 3 месяца назад +4

    I am still not clear, how to manage end to end things like from component to service and call api response and track in component to do further operations based on response. Anyhow i need to use effect.
    Without effect i am not able to get the response.
    Did you get me? Or should I share the code?

    • @larshanisch
      @larshanisch 3 месяца назад +1

      For async stuff you should just use rxjs. That's the right tool for this kind of things. If you want a signal for your request/response, wrap them with some status field (idle, loading, success, error). Use toSignal with "idle" as initial value and emit the other states, when they materialize.

    • @prasoon2510
      @prasoon2510 3 месяца назад

      @@larshanisch but again that will also be a signal, and if any signal gets changed, to get the updated value in component we must use effect right? Or is there any other way around?

    • @larshanisch
      @larshanisch 3 месяца назад

      @@prasoon2510 later this day I will show my little helper function, which I use to turn an async service call into a signal including error handling. Than it will be clear.

    • @prasoon2510
      @prasoon2510 3 месяца назад +1

      @@larshanisch awesome, that might help, else I will share peace of code which I want.

    • @larshanisch
      @larshanisch 3 месяца назад

      Hm, RUclips is not showing all my replys - seems, they don't like links...

  • @samchilvers3711
    @samchilvers3711 2 месяца назад +1

    Have seen people using the input transform function as a setter. Would be interested in your opinion on that approach. I have used it myself and it works well but I can't shake the feeling it is a bit of a hack. Also rather than using allowSignalWrites I tend to use the untracked function. Agree that it's best not to use effects and I do my best to avoid it if I can, like I avoid manually subscribing in RXJS.

  • @gageracer
    @gageracer 2 месяца назад

    I had these desync states with so many tables and data models at work. I really really hated it. Using layers of components and having 3-4 different table components just so someone else thinks they fixed it was insane. It showed that nobody at my office knew how exactly rxjs works. When I did a prototype with something simpler like svelte everyone just yelled at me saying omg its just another framework. Bro you don't even use the one you have properly. either learn the tools and have standard way with your team or use something simpler.

  • @dale0schaffer
    @dale0schaffer 10 дней назад

    what about required inputs? when i try to create a signal that is based from an input that is required, i get an error 'Input is required but no value is available yet'. any ideas how to correct this implementation?

  • @tolstoievski4926
    @tolstoievski4926 3 месяца назад

    I've recently used an effect to setup/remove event listeners on a set of view childrens based on a boolean signal input (no signal writes), I suppose that's a good use case for effect.

  • @larrycoleman8149
    @larrycoleman8149 3 месяца назад

    Can we have reactive template form with this nested reactivity please

  • @avrimalka8798
    @avrimalka8798 20 дней назад

    There is one issue:
    if myName is set to 1 by a parent and child overrides it later, then if parents want to override it again with 1 since myName is now having a different overriden value, it will not work, signal are not allowed to be set with same value even if you set equal: (a,b) => false
    I will not be an issue when using objecr refereances

  • @Poneggen
    @Poneggen 2 месяца назад

    So there's one case which I haven't found an effect free solution for and it is regarding triggering outputs, depending on certain signal values... Is there anything I could do to get rid of the effect, e.g.:
    number = signal(0);
    isEven = output(true);
    effect(() => {
    this.isEven.emit(this.number() % 2 === 0);
    });

    • @ManuelTreuheit
      @ManuelTreuheit 2 месяца назад

      Though using `effect` under the hood you could use `toObservable` together with `outputFromObservable` here with:
      isEven = outputFromObservable(toObservable(this.number));

  • @Michel000000001
    @Michel000000001 2 месяца назад

    So instead of make it explicitly an effect, you put it in an object which has a signal in it and return that object as a computed value, and by that design the index-signal gets reset every time the options change.
    So it is still an effect of changing the value of the options, but then hidden within a (not so obvious imo) code construction.
    And for this to work you also have to wrap 'normal class properties' in another object (in this case called 'state').
    In my opinion this looks more as a workaround than intentional.
    And now the signal for the index gets thrown away and the signal for the options is not, why is that a logical thing to do? why is it logical to recreate signals?

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

    when you select index doesn't it recalculates state because state is not dependent only on options but also on index? so when you select index it recalculates and sets back index to -1
    Also why:
    name = input('')
    myName = computed(()=> signal(this.name()))
    instead of
    public myName = computed(()=> this.name())

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

      Because he wants to be able to override the name at the component level, which you can't do with a computed.

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

      @@wartab jeezzz... do you understand what's happening there? I'm not talking about what he wants - i understand what he wants. I'm talking about bug in his computed implementation.
      When options changes he want to reset index to -1.
      He don't want to reset index to -1 when index changes - but that's what will happen.
      Since computed depend on index and options then when he do select(idx: number) then it changes index to selected index and then computed is recalculated(because index changed and computed depends on index) and index is set back to -1 and that's not what he wants - that's bug in his implementation with computed.

  • @samwight
    @samwight 2 месяца назад

    I like this and I'm okay with this (creating a signal inside a computed is so cool!) but please don't remove the ability to set signals inside of effects lol. There's not a good way to enforce it because you can very very easily write the sets inside of a queueMicrotask (at least I'm 99% sure) and reactive systems cannot track that right now, cause it'll push that logic to happen after the current stack frame (wrapped in the current signal context) is done.
    Having experimented a bunch with Vue over the weekend, their reactivity APIs are significantly nicer because they give you the ability to shoot yourself in the foot with setting signals inside of effects. There are a lot of things that you can do with effects that you can't just do with computeds, especially when needing to write wrappers to turn other data types from other libraries into a signal.

    • @guilhermehenrique3458
      @guilhermehenrique3458 2 месяца назад

      don't think they will remove the option to set signal inside effect. probably they'll just remove the necessity of putting an {allowsignalWrite: true} option. they will make it as default if im not wrong

  • @conradocampetella2253
    @conradocampetella2253 2 месяца назад +1

    So if you have something like this, using the old Inputs.
    _options: WritableSignal;
    _index: WritableSignal;
    @Input() set options(options: string[]) {
    this._options = signal(options);
    this._index = signal(-1)
    }
    It would work in the same way without the signal inside the signal approach

    • @ManuelTreuheit
      @ManuelTreuheit 2 месяца назад +1

      You should never "re-reference" a local signal (or in the rxjs-world an observable). Subscribers to the initial variable will not re-subscribe automatically to the new signal/observable and will lose reference. To reproduce try to include one of your _signals inside an `effect` and log their value. It will log the _options() and _index() once initially and won't log anything later due to the newly created references.

  • @AleksandraSetsumei
    @AleksandraSetsumei 2 месяца назад

    what a beatiful solution

  • @arunsp767
    @arunsp767 3 месяца назад +1

    Effects, State.. My mind is having hard time wrapping around this! When did Angular come up with these?

    • @chrisodillman3355
      @chrisodillman3355 2 месяца назад

      It is in preview since angular 16, but i did not use signals yet too and still stuck with rxjs here 🙂

  • @tiaroque
    @tiaroque 2 месяца назад

    This is really great tips thank you very much!!!

  • @MarekPanti
    @MarekPanti 3 месяца назад

    Definitely

  • @lightyagami5963
    @lightyagami5963 2 месяца назад +1

    I don't think the way this video has introduced is a good practice, actually, you can derive the default selected state without resetting it:
    selected = signal('-1');
    selection = computed(() => options().find(id => id === selected()) ?? null);
    And that's it, so clean so beautiful.

    • @qyihamba7034
      @qyihamba7034 2 месяца назад +1

      How do you reset selected to -1 when options is updated?

    • @lightyagami5963
      @lightyagami5963 2 месяца назад

      @@qyihamba7034 My approach here cannot reset selected to -1, but again not recommend the way the video was introduced.
      "State that updates together should live together". Alex's trick only let the state *live* together, but the way of updating the state is somehow indirection by computed API.
      The best & simplest solution for this is actually reducer mode. It not only lets the state be defined together, but also makes it update together.