A side effect is a change that happens in the state of the app outside of the composable scope. Ideally, composable should be side effects free, but sometimes they become necessary wondering, how do we manage side effects in jet pack, compose? Stay until the end of this blog where we will discover exactly that.
A side effect is a change to the state of the app that happens outside the scope of a composable function, due to composable's life cycle and properties such as unpredictable recompositions, executing recompositions of compostables in different orders or recompositions that can be discarded. Composables should ideally be side effects free.
However, sometimes side effects are necessary. So, what do I even mean? Let's break it down and first understand that what exactly are side effects? So starting with the video, I am having a pretty simple project. As you can see, nothing complex. I'm simply having a view that is pretty similar to the one that we used in the Ultimate Guide to States View Models and Life Cycles blog/vide
So in here you can see that the screen is pretty simple and similar to what we were doing in the State's video. However about this time. If you notice for the incrementing and decrementing functions, I am not doing that inside the composable.
Instead I'm using a view model for incrementing and the decrementing logic. So if I go here, you can see in the increment, but, and the decrement button, I have a fake API call to increment and decrement the counter. So this is the counter state, and here I am, incrementing and decrementing the value.
Nothing complex. So if I go ahead and run the app, you'll see that the app is working just as expected.
Also in the view model, you'll see that I am having a fake API initial call. So this is the API which you'd want to call only one time.
So if I go ahead and let's say I simply call this API and run that up once more.
Okay. When I increment, you'll see that the initial API is being called again and again. Similarly, if I decrement, the API is called again and again. Each time we decrement.
Well, technically this should not happen because I have only called the API once. Well, the reason to this, as most of you might have guessed, is that since on incrementing and decrementing, the composable gets recomposed, it basically re-executes the composable. That is the exact reason we used to use, remember keyword for the states so that when recomposing, the composable, the compiler does not re initializes the state values itself.
So for each click, the compiler, recomposes, the composable due to which the initial API keeps getting called again and again. Now, to solve such use cases, we have side effects. So let's start with the most common side effect that we use, the launched effects. Well first let's use the launched effect.
So to use that, we simply have to wrap the initial API call with the launched effects block. Now if I run the app, you'll see that as soon as the app runs, initial API is called, but when I increment the counter, the API is not called anymore. And similarly, when I decrement the counter, the initial API is not called anymore as well.
Now we'll see that everything seems to work fine, and the initial API is called only once.
1. Launched Effects is executed once when entered inside the composition, and it is canceled when leaving the composition
2. Launched Effects cancels or relaunches When keys or state changes
3. Launched Effects must have at least one key
4. Launched effects work on the main scope. Dispatcher
Now launched effects need at least one key. That means that whenever the value of that key changes, the launched effects block gets canceled and relaunched. Now in our case, we have given the key as true, never changing constants like True or unit are used when we want to run the launched effects only once in the entire duration of the composable life cycle, if you would have passed another key, which was based on a state. So as soon as the state would have changed, the launched effect would have run. Once again, if the launched effect blocks would have already been running, then it would have gotten canceled and relaunched if the state changed.
Go ahead and try that by using a state value as a key for the launched effects. Also, remember to update the state for it to be in. Well, that was about one of the most common side effects, but now let's move on and learn more about the different side effects as well.
Coming onto another side effect, which we use when we have to obtain composition aware scope to launch a co-routine outside of composable. That is called the remember co-routine scope. Now, what do I mean exactly when I say that this is used when we have to obtain composition aware scope to launch a core routine outside of composable?
Well, as launched effect is a composable function. It can only be used inside another composable function in order to launch a co-routine outside of composable, but scoped so. Will automatically be canceled once it leaves the composition we use. Remember Coroutine scope. We also use, Remember Coroutine scope whenever we need to control the life cycle of one or more core routines manually, for example, canceling an animation when a user event happens. It is also a composable function that returns a Coroutine scope bound to the point of the composition where it's called the scope will be canceled when the call leaves the composition. So in here, I am incrementing the value of the counter almost instantly when I hit the increment button. Same goes for decrementing as well, but let's say this was an API call that we had to make when the user taps on the buttons.
In that case, it would take some time to process. Moreover, what if the function calls were suspended? Let's see, what do I mean by that?
Now in here you can see that I have made my functions suspended and also added some delay to fake or real network call. Well now you'll see that this calls for an error in our code.
We cannot call the functions directly. Now, since now the view model functions are suspended. Now we can only call them in a Coroutine, so to do that, I added a Remember Coroutine scope, and then used this scope to launch all the suspend calls.
Also, one thing to note here is that this Coroutine scope would be composition aware. That means this scope is bound to the life cycle of this composable. As soon as this composable gets recomposed, the scope would be canceled and relaunched again. Well, this might be sounding very familiar to you. This is what Launched Effects was doing at the basic right.
So what exactly is the difference between launched effects and remember Coroutine scope? Well, in laymen terms, and as a rule of thumb launched effects should be used when you want that. Some action must be taken when your composable is first launched or relaunched, or when the key parameter has changed.
For example, when you want to request some data from your view model or run some sort of animation. Remember Coroutine scope, on the other hand, is specific to store the Coroutine scope. Code to launch some suspend functions. Remember, Coroutine scope will keep the reference of the Coroutine's scope in a specific point of the composition.
therefore, if a given composable is removed from the recomposition, that core routine will be canceled automatically. For instance, you had the following composable calls A, calls B and B calls C.
If you remember the coroutine scope in C and it is removed from the composition, the core routine is automatically canceled. But if you remember the scope from A, pass the scope through B, and then use the scope and c.
When C is removed from the composition, the Coroutine will continue running because it was remembered in a, so technically the only relation between launched effects and remember Coroutine scope, is that both of them can be used to launch a Coroutine. now. We'll be using both of these effects a lot in this series, so it's better that you take some time to truly grasp the concepts.
You can re-read this section of the blog if you feel that something is unclear, or you can directly reach out to me on my discord server.
just a quick pause here. Just want to tell you that if you're interested in learning about the advanced concepts of jetpack compose such as creating our own animations, gestures, views, and layouts, then do check out my course here.
Now that we have learned about the two most important side effects, let us hop onto the third one. The remember update state. Now, this is used when we want to reference a value in an effect that shouldn't restart if the value changes. Launched effect restarts when one of the key parameter changes.
However, in some situations, we might want to capture a value in our effect that if it changes, we do not want the effect to restart.
In order to do this, it is required to use, remember, updated state to create a reference to this value, which can be captured and updated.
This approach is helpful for effects that contain long lived operations that may be expensive or prohibitive to recreate and restart the official documentation does a great job and gives us a pretty good example to explain this. suppose our app has a landing screen that disappears after some time. Even if the landing screen is recomposed, the effect that waits for some time and notifies that the time has passed shouldn't be restarted to create an effect that matches the life cycle of the call site and never changing constant like unit or true is passed as a parameter In the court shown launched effect true is used to make sure that the on timeout Lambda always contains the latest value. That landing screen was recomposed with on timeout needs to be wrapped with the, remember updated state function, then the return state should be used in the effects.
So with this, we complete the third side effect as well. Technically, now you should be good to go because 90% of your time with jetpack compose, these are the most important and common side effects you'll be encountering on a common basis, However, these are not the only side effects that JetPack compose provides.
There are more side effects provided by JetPack compose for particular use cases, which you can see
right here. I'll have the link to them in the description box down below if you'd want to learn more about them.
So let's keep the blog, Until this point, I hope that you learn something of value in this blog. Thank you for reading the blog and for your time, we'll meet in another blog. Until then, keep contributing code to Humanity.