If you already have a subscription, you can sign in.
Enjoy free content straight from your inbox 💌
00:00
After use stage, perhaps one of the most utilized hooks within React is use effect. In this lesson we'll look at its various creative use cases while covering the features provided by this hook. So let's go. One of the simplest usages of use effect is to register a callback function that gets invoked whenever the component is rendered. To demonstrate what we mean by rendered versus rendering, consider a simple counter component where we have a count and an increment function and within the UI we display the count and a button wide to increment.
00:32
Now, when React runs our function to request the virtual dorm, which we are going to return, this is known as a rendering phase, we can add a console log statement within the function body. And indeed, when we jump into the UI and run it, you can see the console log rendering every single time a component needs to rerender because of, as an example, a state change. Now the use effect hook is something that is exported from the main react module and we can do a simple import to bring it in. And what's great about this particular hook is that it allows us to subscribe to the fact
01:05
that a component has been rendered. What rendered means is that we have returned the virtual do and React has gone ahead and made the real dom match whatever we returned. So if we jump to the ui, you can see that we have two log statements. First is rendering followed by rendered, and this will repeat every single time the component needs to read render, for example, in this particular case because of a state change, and to prove the fact that there is a difference between a rendering and rendered without getting too complicated, let's create a simple utility function
01:38
that gets the real dome count element by ID if it exists and then returns its inner text, and then we log out this real dom count value in rendering as well as rendered. Now, I encourage you to think about what is going to get locked to the console. It should initially be undefined because nothing has been rendered, but once it gets rendered, it should be zero. Then in the next rendering it should be zero. And once it does get rendered, it should be one and so on. And indeed, when we run the application demo, it does match our understanding during rendering.
02:12
It is undefined. Once rendered it is zero in the next cycle during rendering when the dorm hasn't updated it is zero. And once it is updated, it is one. And of course this process of lagging while we are rendering and we have yet to return the updated dorm to react and the real dorm being up to date after we have rendered the use effect callback is going to continue for the other values as well. Use effect also supports a dependency array, which allows you to subscribe to specific changes in your component. Let's consider a simple UI that has multiple states and to demonstrate that we create a simple value state
02:46
and then we create the stage called word count, which you want to recalculate every single time the value changes. We wire the value and the set value to a simple input element, and then if we do have a word count, we display that within a simple div. Now to keep the word count in sync with the value, we would actually want to subscribe to the value and then run some code to update the word count. This is where the dependency array of use effect comes in Super handy use effect takes its dependency array as a second argument in addition to an initial call, because
03:19
there will be no previous values. If any of the members of the dependency arrays change, then the use effect callback will be called again to demonstrate this behavior. Let's just add a simple log statement containing the value object. If we jump to the UI within the console, you can see that there is an initial call because of course this is the first time that value has been set. And then every single time we modify something that is not value, for example, increment the counter, nothing gets logged. But if we do modify the value, which is a dependency for this user effect, then we see the log statement
03:53
again and again. This dependency array allows you to synchronize different data sources. For example, we want to synchronize that whenever the value changes, we want the word count to update as well. Now we have a backend that computes this complicated word count result. For us just getting is not complicated. It's safe for demo purposes, but if the user provides the value of an empty string, we don't need to call that backend. We can just immediately set the word count to zero. Otherwise we make an API call to get the word count. And once that promise resolves, we update the word count state.
04:26
And this works exactly how you would expect. Whenever the value changes, we make that API call and etch has a simple word count of one, whereas two words, hello world, have a word count of two. Now because side effects might be long running tasks, we might need to cancel them to prevent weird behaviors. Use effect has a need solution for canceling or marking tasks that are no longer valid. Now let's add some simple logging to our application. Whenever the result comes back, we log out what was the value for which we just calculated the result. Now there is no guarantee
05:00
that the results coming back from get word count are going to be in the order in which the requests were made. And this is going to be true for any request that you are making to an external server. And we can see this nasty bug pop up within the ui when we make a request for Hello World, we get a word count of two, but then it changes back to one and we can deduce the reason why this is happening quite easily thanks to the log statement that we added the result for Hello is actually coming back after the result for Hello World because of some network latency. And so we are seeing the result for Hello,
05:32
even though the user has moved on and the inputs currently has the string, hello world. Now the solution to this problem is pretty easy. We have two options. One is to find some way to cancel the task or alternatively even simpler. Simply mark the fact that we no longer cared about a particular result. We create a bullion canceled variable, which we initialize to false as the task hasn't been canceled yet. And then within use effect, we return a cleanup function where we mark the task as canceled. When we return a function from use effect, use effect treats it as a cleanup task.
06:06
If a newer call is made to the same use effect because of a change in dependency or the component is completely going away, then this particular cleanup function will get called by use effect. So by creating a simple bullion variable and setting its value within the cleanup, we can use it anywhere within our function to see if a particular task has been canceled because it has been superseded by something new or the component is no longer relevant. Here we are using the canceled variable to make sure that we do not set the word count. If this particular use effect has been cleaned up now within the ui, we still get the results in the various order
06:40
that we would get them in. However, we do not leave the UI in an inconsistent state. We safely disregard the result for Hello because it has been superseded by newer values. Beyond observing simple values, we can actually make creative use of the dependency array to tie into additional lifecycle events like our components getting mounted and unmounted. Now I understand that all of these usages of use effect can become a bit overwhelming. So here's a quick recap and an expansion. With a kitchen sink example, we have two straight variables, account and a value.
07:13
And with a simple call to use effect without any dependency array, we can register a callback for every single time the component gets rented by react to the real browser Tom. Now with the dependency array version of use effect, we can register a callback that gets invoked every single time the component is rendered because of a change in the provided dependency array values. And we can of course return a cleanup function from either of these calls if we wanted to. This brings us to the third usage of use effect where we can actually register for a component getting mounted
07:45
and unmounted when we provide an empty dependency array. Now, this is not something that is new. This is actually a special case of the dependency array version. We know that the dependency array version does get invoked even initially, and this is true for the empty array version as well. The difference is that because of an empty array, it'll never get invoked again. So essentially it becomes for the first time the component gets rendered, which is also known as the component getting mounted. And we also know that the cleanup function gets invoked
08:18
every time there is a new call or when the component goes away. So with an empty dependency array, there are no new calls and the only time the cleanup function will get called is when the component will go away, which is also known as the component getting unmounted To demo the different behaviors of these use effects, we create a simple UI that displays the count and increment button and an input wire to the value state. Now, in order to demo the component getting mounted and unmounted within our app, we create a simple Boolean state shown which we initialize to true, and then to toggle it between true
08:52
and false, we create a simple button Wire to set shown, and then when the shown bullion is true, we will render architect sink. Now with the simple code to mount and unmount the component, let's focus in on architect sink example and observe its behavior with the browser side by side. You can see that initially when our component gets rendered, we get the callback for rendered, we get the callback for the new value, and we get the callback for the component getting mounted. And whenever the component read renders, for example, because of a change in the state count,
09:26
only the rendered callback will get called again and none of the other ones do because their dependency arrays have not changed. Now, if the user changes the value state, we will get two callbacks. One, because of the fact that the component has rended and one because of the fact that the value has changed, which means that the second use effect also gets called. But before these two new calls, we also get a call for the cleanup for the previous value, which of course was empty. And finally, when we unmount the component by completely removing it from the dorm, all of the cleanup functions will get called.
10:00
We get the cleanup for the old value and we get the cleanup for the initial use effect call that got in workeded because of an empty array. So an empty dependency array is not something very special, but it is used commonly enough that it is good to develop a visual memory for the fact that when there is an empty dependency array, we either are listening to the component getting mounted or we want to listen in when the component gets unmounted. The ability of use effect to transparency tied to a component lifecycle allows you to do some pretty powerful stuff and create some pretty unique custom hooks
10:33
to demonstrate this fact. Consider the desire to listen in on the window width. We can get the window width from a window dot innovate, and we can use this value for responsive design by using it when we are rendering our component. Now, right now, this is actually just a snapshot of the value that the window in width had when the rendering for the component was happening. And we can prove this by playing around with the UI and you can see that the value doesn't actually react. Now, to fix this, you would imagine that we would want the window width to be dynamic, so of course we will put it behind a use state hook.
11:07
Now we would want to start listening to the window width changing as soon as the component gets mounted. And for that we have the use effect hook with an empty dependency array, we create a utility function, which we want to invoke whenever the window gets resized. And within this function, of course, we will update the window width state variable with window inner width. Now to subscribe to a change in the window width, we add an event listener on the resize event for window and invoke handle resize. Now, one thing that you should always do
11:38
whenever you add an event listener is to make sure that you remove it when you no longer need it. In our particular case, we are subscribing when the component gets mounted, so we would want to remove this event listener when the component gets unmounted. And we do that in the use effect cleanup function, and that's it. We've effectively made a responsive window where it's variable and we can see it react as we resize the window. Now, this is all neat and tidy, however, it's still a lot of code for a simple variable, and we wouldn't want to repeat this code again
12:10
and again every time we want to listen in our window width. So wouldn't it be great if we could create a custom hook that abstracted all of this complexity away from the app component? Fortunately, creating a custom hook is a super easy task. We take all of this functionality that we wrote within app and then move it within a custom hook function and just return the value that the app component expects. And within the app component, we make a simple call to our custom hook and then get the window widths from the result. And now we have a nice responsive hook that we can use
12:45
whenever we want to listen in on the window Widths. Using a use effect with an empty dependency array allows us to transparently tie in when the component gets mounted and unmounted. I'll wrap things there. Thank you for joining me and I'll see you in the next one.