Goodbye, useEffect - David Khourshid

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right all right hello everyone hello react Brussels it's been many years since I've been in Belgium so I'm so glad to be back here again this is basically going to be a crash course where we are going to learn the right way to use effect and saying goodbye to all of the other use effects that we really don't need in our application so buckle up we are we are in for a ride hopefully you will learn a lot of things today but first of all I'm extremely happy to be here who's happy to be here at reacts Brussels yeah all right who's uh who's a little sad that you know it's it's ending so soon I mean we we had we had a lot of amazing talks like some of my favorite talks today uh how many of you feel like this have you seen this smiley before this is the frustrated react developer Smiley so yeah so of course take care now I I learned hooks I um I I was actually fortunate enough to get a preview of hooks when they first came out and they all looked really interesting except use effect I'm like all right that's gonna be an interesting one and then uh you know people started saying okay use the fact I have an effect this is where it goes but then the problems kept creeping up and I'm sure in your code bases if you're using react uh you know you might have some really nasty use effects in there but if you're struggling with use effects don't worry even senior react developers struggle with it if anyone tells you otherwise they're not making you know good enough react applications because you do need use effects in your application and you might have this big nasty dependency array and if statements it's all very typical it's just a really awkward hook all right so uh typically this is what our you know use effects looks like we have a use effect that does something we have a cleanup dependencies all is fine you know it might have worked before but then guess what react 18 came out and it rendered this again so it ran the effects twice on Mount in straight mode how many of you have actually ran into that or is that just a me problem okay a bunch of you so uh use the fact if I were to summarize this talking just one slide it's not for all effects in fact I would say it's even confusingly named it shouldn't be used effect it should be used synchronized but we're going to be talking about that in a minute so what was life before like before use effects well we had component did Mount component did update and component will unmount and these were all convenient places to put your effects especially the effects that were long lived inside of your class-based react components and so when we moved to hooks thankfully uh we we need to figure out okay I have these please these effects in my life cycle hooks so where do they go now well we put all of our componented mounts effects inside of this one with an empty dependency array we put the ones where it happened on component did update in a non-empty dependency arrayed use effects and then this awkward looking uh component will unmount it goes in use effects with this return function and this just looks really really awkward so I want to say first and foremost that use effect is not a life cycle hook and while you could use it that way and it is expected to run at the exact same time as you mount your component unmounts and change it's not the way that you should be thinking about things uh Danny abramov even said that the mental model of use effect is synchronization not life cycle and so we're going to be talking about why exactly that is all right so uh you know we have our use effects we have the effect in there we're doing something and the dependency array it's uh you know this is when that effect is going to be executed now react is supposed to be this declarative framework but honestly this might feel like okay we're doing our effects in a declarative way but in reality there is a ton of indirection over here so take the imperative approach first like when something happens like an event or you know something else a button is pushed a form is submitted execute this effect you might be looking at this and saying okay that's the imperative approach the declarative approach with use effect looks more like this when something happens and uh you know it causes the state to change then depending on which parts of that state change which is the dependency array this effect should be executed see how it's more declarative right but only if some condition is true inside of my use effects also reacts May execute it again for some future reason but only in strict mode which you shouldn't disable for some future reason so I mean honestly whatever your opinion is on imperative versus declarative I prefer the imperative approach and you could express it in this imperative approach in a declarative way we'll be talking about that later too but some of you might be thinking you know what I didn't really have a problem with use effects like I just do something I return a cleanup and I put things in my dependency array everything is fine but the more you add features and and changes to your application your use effects start to look more like this where we have a bigger dependency array we have multiple use effects we forget the cleanup and we have this nasty conditional in here and we're doing things depending on what's inside there so we're trying to control the fire hose of changes happening so that our effects happens at exactly the right time and I've seen this code all over the place I've searched Source graph for examples and there's just plenty of examples this is a particularly nasty one and so it really uh it really showed me that dependencies are really the wrong mental model for effects we want effects to execute when things happen not exactly when things change so let's get back to the original issue react 18 running effects twice on mounts in strict mode how do we fix this in first of all why does this happen so react is actually doing this on purpose behind the scenes it's not a bug or anything it's mounting the components and then it's doing a simulated unmount of that component which is calling the cleanup of the effect and immediately afterwards it's remounting uh the effect so um you get your double execution of the effects and so uh you know when I first ran into this I was like this is a bad hook it's use defect you know it feels like a major major foot gun but honestly this was reacts way at least react to 18's way of telling you hey you're using the use effect hook wrong so what is use effects for it's for synchronization and so there's a difference between fire and forget effects and synchronizing something in your component with some outside source so uh take this example we're synchronizing with some external API some story API so that uh whenever the item changes we're we're getting this item data and we return an uh an unsubscribe function and this is going to change whenever our item changes because we need to unsubscribe to the old item and re-subscribe to the new item now doing something like this means because you're just keeping track of some external thing we could unsubscribe and resubscribe multiple times it doesn't matter it's like watching TV if you put on the news you could turn the TV on and off you could change channels you could go back to the previous channel that news channel is still going to be the same so that's sort of the mental model that you should be thinking of with use effects um the react documentation distinguishes between effects and event handlers so uh when react is talking about effects with the capital E they're talking about long-lived effects so these subscriptions that I'm talking about when uh we're talking about fire and forget effects uh reacts in the docs mentions that as event handlers so here's the difference a fire and forget effect you don't care what the results of this could be a console log an analytics call maybe you're sending some data asynchronously but you're not awaiting a response a synchronized effect is when you're actually communicating with an external system so this effect could actually be long-lived and so that's why it's fine for it to unmount and remounts because just like changing channels on the TV you are just changing um you know like you're changing the subscription but you still get the same values okay so where exactly do the action effects those fire and forget effects where exactly do those go we know that you know we shouldn't really be putting side effects in render even though in some instances it's actually okay but let's just assume that you can't do that and we already know by now hopefully that putting it in use effect is awkward so where where else do we have space in this function to put it and don't say the gap between that because I just forgot to fill that in outside the component Maybe let's explore this so when we um when we mentioned that that the um that use effect is going to run twice when you mount a component here's what's happening we're attaching that effect to the components mounting and unmounting so when an event handler causes a state change that is going to cause the components to re-render but also remember that react can remount that component and so this effect might be executing multiple times so if we put it outside of the render say over here it doesn't matter how many times that component's unmounts and remounts and how often react re-executes that use effect the effects that we put outside of the component rendering and outside of use effect is only going to be executed once so these action event effects should actually happen outside of rendering so then where exactly do action effects go inside of event handlers at least sort of so we have this submit data inside of this form on submit event handler and this is actually going to be a much better pattern than trying to do this in the use effect and we're going to see an example of this shortly so by doing it this way you have this pattern of when an event happens in effect is executed and so uh if you do it using use effects then this is the mental model when an effect happens you're changing some states which can trigger an effect because that effect is listening for a state change and especially if you have multiple use effects other states may change and you get a chain of triggered State changes which might trigger another effect and this becomes really really hard to visualize really hard to think about all right so everything I'm going to explain including when to execute action effects and uh when to use use effects this is all actually explained in the new react docs at beta.reactsjs.org uh when I first started giving a talk like this these actually did not exist so I'm actually really thankful if you go to the normal reacts.js.org documentation site they have a nice Banner on top that points you to beta.react.js.org I Really encourage you to visit it because there's a lot of great information in there and one of my favorite sections is you might not need an effect so I'm going to explain to you all of the different points that they make first one is you don't need a use effect for transforming data I mentioned use memo gear you don't even have to use memo so let's take this example we have a cart we're keeping track of the items we're keeping track of the total and so whenever the items change we have to update the total right your first thought might be okay that's an effect when items change set the total to the new number of items but guess what you could do this directly inside of the uh inside the function so a derived value is just you're reducing the items or if you don't want to use reduce you could have a function that just calculates the total price but you could do it directly inside of the components and if this is an expensive operation and only if it's an expensive operation you could take this and you could put it inside of a used memo if you really want to don't overload your components with use memo because uh You Know You're basically trading performance for memory and yeah only use this when you really need to when you have expensive calculations but yeah you don't need use effects to calculate derived State and I actually ran into this problem like basically whenever you see a set State inside of a use effect it should be sort of a warning sign to you especially if this happens I've had this happen before I've had it happen with a fetch which is really terrible because you know that makes a million API calls so much that react has actually had to add a warning saying like hey this is rendering a thousand times and you're probably doing something wrong and so that's why you know some people think that the default behavior of use effect is an infinite Loop but this is now mentioned in the beta docs that this is actually a pitfall and you should not be setting States or at least you should avoid sending State inside of use effect and also don't forget that dependency array it's extremely important all right second thing you don't need use effects for communicating with parents so sometimes we have this pattern where we have on open and on closed in the parents and so depending on the state of something we are calling on open or on close uh so the problem with this is that you you might be accidentally introducing additional renders because uh the use effects because of a state change it's re-rendering and then it is in the next um the next cycle like when use effect is called it is telling the parents to do something which might tell the component to do something so you might be introducing additional re-renders inside of there so a better pattern to do is move these effects like on open on closed so these fire and forget effects move them closer to where the state change actually happens so I'm calculating if it's going to be open or if it's going to be closed I'm keeping track of state which I might not actually need to do here and then depending on whether it's open or closed I called directly in the event handler this toggle view event handler on open or on closed and you could have also when you do that you could easily refactor it to a separate hook and then you have this nice used toggle hook that you could just use directly in your component it's a much much simpler way to uh to express that logic and to contain it in a in a in a hook all right so third thing you don't need use effects for subscribing to external stores so I know what you're thinking like I just said that this is the one thing that you should use use effect for and it is true most of the time however there is a really cool hook that was mentioned in the last Talk use sync external store and um you might think okay that's a very complicated sounding hook and it seems like it's for advanced use cases for Library authors but it actually makes things a lot simpler so going back to a previous example where we're subscribing to a story API I'm checking for the connection status and I'm setting is connected which again if you're setting State over here it might be a warning sign that you might be able to refactor out this used effect and so I'm subscribing to it that I'm calling unsubscribe and I'm reading data from this uh store API so instead of doing this I could use the use sync external store hook and it actually makes things a lot simpler because we're getting is connected from this snapshot over here this door API dot get status and we're reading whether it's connected or not so this is going to be a Boolean we pass in the subscription function or the function that will actually subscribe to the store and we also provide the server snapshots so this could run uh on server side rendering all right this one is actually a big one because this accounts to like so many of my previous uses of use effect which is fetching data we think okay I need to fetch data I need to call an API so I'm just going to stick it in the in an effect but the better way to do this is actually render as you fetch so here's an example you all have probably seen this a hundred times where we have to do this weird dance of uh having a cancellation flag and then we get our items and you know if it's canceled so if we navigate away or we don't want to call This Promise anymore we're going to ignore the response from that promise and then we set the item switch again if you see this in the use effect it's a warning sign and then we return this cleanup where if anything changes is canceled is true also I forgot the dependency array I legitimately forgot it I just realized that now so this is a very very bad use effect all right so what are some Alternatives well uh like was mentioned in previous talks remix has a great API so long story short Frameworks use whatever the Frameworks provide you and if you don't have that then use Query works well too but uh with remix you could Define a loader function and you just get your items and that's going to be available to your component immediately you don't have to do that use effect dance next JS has something similar with get server side props so you could just awake get items in that function and return it as props over here into your actual components and so react query I'm sure a lot of you are using react query it's an excellent Library it's not a state management Library it's not even the fetching Library it's a caching library that works really well with fetching data so um in in this case I'm actually doing prefetch so I'm prefetching this data I'm populating it in and then I'm reading it so instead of use effect in summary you should either use Query or use a hook like that or use a framework or use another hook that was actually recently it's not out yet it's it was recently announced its use just use so I I'm serious you might be thinking like what kind of hook name is this I mean it's a legitimate hook name but it is actually an RFC first class support for promises and async the weight and so this is actually probably maybe going to be the future of using suspense inside of react so basically you would fetch a post and um you would have some sort of caching mechanism which react is still trying to figure out but again this is how use Query Works under the hood as well and so this is going to automatically suspend your components so use it seems really cool terribly named um but you know that's the future all right and so Danny ibrahimov actually said there's a lot of problems with fetching and use effect you might have race conditions no instant back button so basically when you um go back to a page you have to load that data again um no initial HTML content when you render your page you're going to see a lot of nasty loading Spinners and also chasing waterfalls so if you're fetching inside of your parent component you have to wait for that and then when your child component renders after that's done touching you have to wait for that it gets really annoying um there's also the fact that you don't need to use effect for initializing Global Singletons so um for example we know that this is now going to run twice so instead of it uh running twice we could use a ref to track whether it was called or not but that gets really annoying um and so we could you know just move that ref outside of the component especially if you're running this effect inside of the root components uh but you're still using a use effect so instead honestly just call it outside of the component there's really no harm in it now you might be you know saying what about server side rendering what about testing just move it into a function the point of it is that you don't need to put everything inside of a component and so finally the one that I really want to get into you don't need to use use effects for handling user events so like we talked about before this is submitting logic when we submit a form uh it looks very similar to The Promise example except now we have extra variables for whether it's loading whether it's canceled we also have to keep track of Errors instead of this you could just put it inside of a side or a side effect inside of the event handler so this is on submit right over here and this you know it also gets pretty nasty so that's why I would say that when you are working on logic like this you should extract it out to a hook and so my favorite hooks are of this form where we get the states and we could also send in events some of you might know me because I created X date this is how xdate works this is how Redux works this is how use reducer works and it is a very very useful API because all of our actions and events that happen are reduced to a single line where you're just sending an event object so it makes things very clear and very easy and succinct and so I'll actually give you a really quick demo of this um including like just a you know a use effect demo so this is an example application and here's how it works when I click the video it opens and plays when I click outside of it it closes and when the video is finished right now it immediately closes so um right now um actually in the back can you all see these use effects over here can you read them I I can't read them either because there's too many of them and uh they're really really complicated so I I have a bunch of use effects here this one is controlling whether the video is playing or paused this one is controlling whether the video has ended and you know we have that nasty if statement in there uh and this one is controlling whether we have pressed the Escape key because I could press the Escape key and close it now I also want the user behavior of when the video ends I wanted to wait two seconds before closing and so that that's probably going to be another use effect we need to add code to you know all of the other three use effects just to make sure that we're handling those correctly and we're not closing it prematurely uh so instead I want to show you by ways of diagramming how I would model this instead so here uh right now we have the behavior where we start in the mini state where our video player is nice and small and on some toggle we transition to a full state so you know we just toggle back and forth we could toggle like this now in the full State this is what I mean by declarative effects so um in full when this is open I actually want to play the video so I'm just going to add this as an action like that let me zoom in and when I exit the full so when I go from Full to mini I want to pause the video because the video might not be done so I just want to make sure it's being paused so this it's sort of like a flowchart you could Envision how it works like we're mini and then when we click we're in the full mode where we see the video playing and when we toggle we're back to Mini now uh there's a couple of other things that we could do so um I'm going to add an invocation and so basically we're going to add some listeners this is going to uh listen for um whether the video ended or not and I also want to listen for um or actually before that I want to do this so we are going to also add events for video ended and we are going to add an event for he dot Escape all right so this is our logic so far so when we're in full mode whether the video ends or you press the Escape key or you click outside the video it should go back to mini and it should pause the video so you know that makes sense so far so um I I actually also want the additional behavior of when I'm in the full States um like let's just say that this is playing I also want to um wait for two seconds so how can we do this in like this diagram so I could just you know have a state for when it stopped and I could move this video dot ended I'm going to move this over here drag it a little and so when the video stops here's the logic and this is logic that you might be sharing with a designer or a project manager that when the video stops I want to wait for two seconds so oops let me reload that real quick all right so I want to wait huh weird uh I want to wait for two seconds so here's how we can do that pretend you didn't see that all right so after I'm gonna do two seconds I'm going to transition to mini so if you're trying to explain this logic to someone you could simulate it here and say we're going to toggle to full and then when you press either toggle or key Escape it goes back to mini but when the video ends then after two seconds we want it to close and go back to the mini state where the video is paused and it's you know where we're no longer seeing the full video so a couple of other things I have to add in order to listen to these events key escaping video ended I need to um add some invocations so video ended and also uh key Escape so basically we're listening for these two things to happen and this is our complete Logic for how I want this to work now I only have a minute and 44 seconds so let's see how quickly I could live code this thing in fact actually I probably don't have time to do that so I am just going to copy the code over here and right now this is not working no magic tricks okay not working so I'm gonna go here paste this machine in this is an X8 machine and um oh I I forgot something actually it literally says David don't forget this so oops all right Don't Clap yet um all right so we so when I click the player I have to send the toggle event and that's all I have to do so hopefully this works if I click here if I click outside if I click here if I press escape and if I click here and I wait for the video to end give it two seconds one two close there we go [Applause] all right so this is now available at stately.ai studio and uh we actually released our v1.0 today so please check this out if you if you want to um but yeah I'm really excited that we have this out so um in short just to summarize because I have 20 seconds when do effects happen in these State transitions where an event is occurring and then you model that as an effect so where do these action effects go inside of these State transitions which most of the time happen to be executed at the same time as those event handlers so in short use effect is not for all effects it's for synchronization State transitions are what trigger effects action effects go in event handlers you should render as you Fetch and also it might be nice to model effects with steam machines so thank you very much react Brussels
Info
Channel: BeJS
Views: 331,594
Rating: undefined out of 5
Keywords:
Id: bGzanfKVFeU
Channel Id: undefined
Length: 29min 58sec (1798 seconds)
Published: Wed Nov 16 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.