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

Поділитися
Вставка
  • Опубліковано 15 гру 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.❤

  • @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.

  • @JaiKrsh
    @JaiKrsh 3 місяці тому +24

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

    • @MaratMartirosyan-qx5qx
      @MaratMartirosyan-qx5qx 2 місяці тому +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! 🤗 ua-cam.com/video/sQ96BREhAJg/v-deo.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 3 дні тому

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

  • @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!

  • @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 13 днів тому

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

  • @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 Місяць тому +1

      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.

  • @nathanaelmayne5424
    @nathanaelmayne5424 Місяць тому +1

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

  • @hgoebl
    @hgoebl 7 днів тому +3

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

  • @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.

  • @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?

  • @Matrium0
    @Matrium0 2 місяці тому +1

    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 7 днів тому

    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.

  • @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, UA-cam is not showing all my replys - seems, they don't like links...

  • @dale0schaffer
    @dale0schaffer 5 днів тому

    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?

  • @UfuUfu-sj3bv
    @UfuUfu-sj3bv 2 місяці тому

    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! 🤗

  • @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.

  • @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?

  • @tranquility_lane
    @tranquility_lane 2 місяці тому

    Great explanation, thank you!

  • @larrycoleman8149
    @larrycoleman8149 3 місяці тому

    Can we have reactive template form with this nested reactivity please

  • @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 🥰

  • @andreymoskalenko3706
    @andreymoskalenko3706 2 місяці тому

    Very useful, thanks Alex and Bonnie 😊

    • @techstacknation
      @techstacknation  2 місяці тому

      Thank YOU for the appreciation, Andrey 🥰

  • @avrimalka8798
    @avrimalka8798 15 днів тому

    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

  • @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.

  • @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.

  • @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.

  • @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));

  • @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 🙂

  • @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

  • @AleksandraSetsumei
    @AleksandraSetsumei 2 місяці тому

    what a beatiful solution

  • @MarekPanti
    @MarekPanti 3 місяці тому

    Definitely

  • @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.

  • @tiaroque
    @tiaroque 2 місяці тому

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

  • @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.