Full Guide to Jetpack Compose Effect Handlers

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys welcome back to a new video in this video i will give you all really all the information you need to know about jetpack compose effect handlers i already do have a video about that in my jetpack compose basics playlist which i made very early when compose just got stable and that video is okay however i got quite some comments of people who did not really understand what i was talking about and nowadays i also know a little bit more about these effect handlers and especially about the use cases because yeah i was just now using compose for yeah more than half a year and i think i can now just make a better video about it a more detailed video where i will just go through all these effect handlers that there are you will find jump marks to jump to these and i will of course also first of all explain what that actually is and why we need this so first of all to understand effect handlers we need to understand what a so-called side effect is so side effects are in terms of compose at least are something that escapes the scope of a composable function and that sounds very complex right now i know so i think we better just jump into android studio and take a look so i can give you an example of what that is here you can see a very simple example of a side effect we have a private variable i which is simply set to zero and we increase this here in our composable button scope so you can see we have some state here just text state and when we click the button we add a hashtag and we display this here in our text so what's the problem of this code well the problem is that we increase i by one here and that would be considered a side effect because that is something that escapes the scope of a composable function this eye has nothing to do with japan compose it's not a compose state or anything else we would want to do here and the problem is like this is of course a super simple example but imagine this would be a network call you would simply call a network function here and then whenever this function here actually recomposes you would actually re-execute your network call and you have no influence um about how often and when this function will actually recompose and yeah re-trigger the network call in this case in this case specifically whenever we actually attach a hashtag here to the text state this would mean that this block of code would recompose and it would simply increase i by one whenever the text changes however we don't have in we don't have any influence on that it could of course also recompose more of so that's something you really want to avoid to just do non-composed things in compose code so what can we do to actually avoid this we can use effect handlers because something sometimes we just have to do these things that would escape the scope of a composable function for example if you want to collect the flow or so but yeah directly doing this in the composable is a terrible practice really never do this however we have these effect handlers here which i will now go through step by step i will explain all of them as i said you will find jump marks and after this video you will really know and understand the different effect handlers when you should use which which are rather important which are rather rarely used so you will have a really good understanding about these after this video so make sure to watch it till the end of course the first effect handler that i want to talk about and that's probably the most commonly used one is called launched effect and if you watch my videos then you will probably already have seen me use launch effect before let's actually delete this stuff here and create a launch defect block for that we can simply say launch effect and you can say that takes a key and a block so let's actually remove the block here and just use a normal lambda lambda block here like this to omit the parameter and now what does that actually do what does that key mean or why does it say key one and not key two so first of all we get a curtain scope here you can see that's a curtain scope so we can execute suspending functions in here we can say delay and we delay this for a second for example this launch effect block here is essentially a composable so if we hit control q or just hover over it you can see that's a composable so we are safe to actually use that in a composable function or we rather can only use that in a composable function now this key is something we can pass to this launch defect block and that could for example be a state in compose so if we now use our text state here what that would mean is whenever this text state changes this curtain would be cancelled and relaunched with the new text value so if you delay this for a second and then you print the text is text then whenever the text actually changes for example when we add a hashtag like we did before then the curtain would be cancelled and relaunched so it would simply cancel the delay if it's currently running and would relaunch the curtain with the updated text so the one that contains one more hashtag and yeah unless we don't change it again it would then print this text here after a second that's a very abstract example and i know you will probably not get why this could be useful so that's why i prepared two simple examples here example one is actually if we have a view model here you can see launch effect view model and very commonly we have a shared flow in view models in which you simply send events so you can see we have screen events for whatever screen that is for example if you want to show snack bar or navigate to a different screen we can send these events from the view model to the ui so here you can see we emit a new event show snack by event and we want to show snackbar with a text hello world on our screen so how can we now collect this flow in our composable because if we would just do shirtflow.collect in the composable function itself that would be a side effect because the collect function would be called with every single recomposition and we can control that so what we can do is if we take a look here we can simply use a launch defect block passing through as a key quite true well if we pass through we make sure it's actually only called once so the first time the the composable here is um composing this launch effect block is executing and it's actually never cancelled and relaunched unless the composable here actually leaves the composition and in here we are now safe to actually collect the shared flow because this is now not executed with every recomposition anymore instead only the first time this composable actually enters the screen or is composed for the first time and then we can safely collect these events here and do something with these like show or snack bar or use our nav controller to perform the navigation another example that would actually use such a key here would be to launch an animation so if we take a look here in this composable it actually gets a counter and then creates an animatable out of that counter which starts at the initial value zero we sadly have to use floats here even though it's an integer and then we can use a launch defect block using this counter state and whenever this counter state now changes we will cancel this line and actually relaunch it with the new value so if you have for example some different screens that's actually something i used in my recent course about a multi-module architecture there i actually had values like also such a counter value that reflected the current calorie count of a specific user and depending on the current day the user has selected we actually change this counter so for example for yesterday it could be five hundred calories then for today it could be a thousand eight hundred and in between these days like if we switch we want to animate from the old value to the new value so it just quickly counts out and we have a simple counter animation so that case as soon as we call this composable function with a new counter value it would actually cancel the old animation and launch a new one where it will animate the existing animatable with the current value to the new counter value and as you can see the lma2 function is a suspend function so we have to execute it in a curtain which we actually get here with launch defect so that's an excellent use case for actually using a launched effect and i hope that's now a lot clearer when we can actually use this and for which things that makes sense let's get to the next effect handler and that is remember creatine scope which i prepared something here you can see with remember curtin scope which is also composable function we can get a reference to a curtain scope that is aware of the composition here so as soon as this this composable here is actually actually leaves the composition the curtin here in all the curtains in the scope are actually cancelled so if you actually for example click on a button here as you can see you can simply launch a curtain inside of a composable function um do yeah any kind of suspending calls to network calls whatever and you're actually safe to do that important here is that you only use this in callbacks and with callbacks i mean things like on click here just things that don't have anything to do with the actual composable so you don't want to do something like this and do your network call here for example um it even gives us an error um yeah it it just says that yeah even it should be done in launch defect or just in in our callback here like on click because if you do this then it would you would again have the side effect that you don't want that you want to prevent because with every recomposition you would launch a new curtin here which is very very bad so only use it in on click in on text change or whatever just these typical callback functions and then you're good to go however you actually not really need this scope here just rarely if you need to launch an independent routine maybe in a launch effect block but usually since you if you have a solid architecture with a view model then you can do all that via the view model since it already has a view model scope and usually you much rather want to use that so yeah it's not that often needed let's get to the next effect handler and that is remember updated state here i will take this the example they actually also have in the android documentation because i like that example i think it makes it pretty clear so imagine you have a splash screen and that would be your splash screen composable and then you have a lambda function on timeout that gets triggered as soon as yeah your your delay for the splash screen actually finished so you show it first for three seconds and then you call on timeout you of course only want to do this once so you pass true for the key here in launch effect what's the issue currently with this code well the issue is if this composable function gets actually called with a different timeout function then this launched effect block wouldn't actually consider the new timeout because it was launched with the old one and since we don't pass this on timeout function for the key because we don't want this block to be relaunched when on timer changes which would then extend the duration of our splash screen because of that it would actually all not really override this on timeout function and for that we have something called remember updated state so we can say val um maybe update it on timeout and that's equal to or that's rather we can use the delegate way here by remember updated state and we pass our own timeout function here and that will just make sure to also update this um updated on timeout value here whenever on timeout changes and it will be considered here as long as we actually use that so updated on timeout by doing that we're good to go so this will still only be executed for three seconds in total it won't be relaunched but it will consider new values to this lambda function in case we actually pass these then the next very commonly needed effect handler is disposable effect let's take a look at that so right now here we actually just have a typical lifecycle event observer which is used which can be used in a composable function to simply intercept these lifecycle events so when your activity actually goes in the pause state you can simply have this function that gets triggered check okay is it currently in on pause and if so we simply print the line or do whatever you want to do what's the issue currently with this code there are two issues on the one hand of course we directly declare these observers here like this this observer directly in the composable so it would be declared with every single recomposition which we don't want however we could put this in launch defect right no we can't because this observer is an observer that needs to be cleaned up after being used like if the composer believes the composition we don't want to use this observer anymore so we need to dispose it how can we do this we can't do this with launch defect instead we need something called disposable effect it's similar to launch effect we can also pass a key here in this case we want to pass our live cycle owner um which yeah so this disposal effect is just relaunched whenever the lifecycle owner changes and then in here we can simply put the observer and you can see we get a big error here because this posable effect has the requirement that we implement the on dispose function so first of all we actually want to make sure that we add this observer to our live cycle owner actually otherwise it's not considered like this add observer live actually absorber and yeah that so far so good so this will now make sure that we get these events however it does not make sure to actually clean this up for that we actually have uh lifecycle owner lifecycle remove observer so this needs to be called when the composable leaves the composition however how can we do this now well with this posable effect we have the requirement that it actually that it's actually finished off with an on this pose block and this will be called exactly in that case so when the composable leaves the composition it will make sure to actually remove this observer here before so there is nothing leaking so in general whenever you have something that you need in a composable that requires some kind of cleanup which is typically the case with callbacks then you want to use disposable effect and a very common use case of that it's just to intercept lifecycle events but there are more use cases of course then the next effect handler is a very simple one and that is side effect and that is you can see it doesn't take any parameters it's just a lambda block here side effect gets called whenever your composable is successfully recomposed so pretty simple the use case for that is if you have some kind of non-compose code let's say you have an integer here that's not composed state maybe you get that from an api like a third party library or whatever and that's not state for whatever reason and you actually need to treat it like a state maybe you need to update some some other thing in your code whatever with this non-composed counter whenever your composable recomposes then you can use side effect and actually do this in here so the android team used an example in their documentation that used like in firebase user which is not a composed state and whenever your composer recomposes they just need to make sure to update the analytics firebase user id or so it's something that's really not necessary that often um but it's still worth mentioning here that this exists and in the end it just gets called whenever the the composable is successfully recomposed which is the most important thing here so if you need this behavior use a side effect the next one is called produce state let's take a look in that that's a composable function and it returns this produce state function here let's see what that does that function gives us a curtain scope just like launched effect so in here we can call suspend functions as you can see here with delay and the purpose of this function is just to produce some kind of state that changes over time so it's yeah it's kind of similar to a flow so if we want to construct a flow then we can also emit multiple values of a period of time and this is pretty similar here so we start at a value 0 and then as long as that value is less than the value we passed here we simply increase the value and wait for a second so we just count up to that value every second that's all that happens here and every time we increase the value here it will actually also update the state this function returns so it's in the end pretty similar to just using a flow here flow of type integer and then instead of a value instead of increasing the value here we can just actually we can create a value here that starts at zero we can omit the initial value you increase the value we emit the new value in every second and then we just call that collect as state so the initial value is zero and then we actually also don't need to emit this here so that would be the equivalent in form of a flow but yeah so that we don't need to use this collected state function we can also use produce state which is the yeah the more composed way i would say just that you've seen this quite handy in some use cases so yeah let's get to the next one which is a little bit more tricky that's called derived state off which you can't see yet here but let's actually take a look at this code first so we have a counter state that starts at zero then we have a counter text that just says the counter is whatever value you have on that counter when we click on this button we increase the counter and just show the updated text here so as soon as the count is actually increased this text will actually be recomputed and we'll just update and the text here will update as well however what's happening here behind the scenes is that whenever the counter is updated this text here is really recomputed so this simple example is really not an issue but recomputed here means in form of the string that this is concatenated together again so it's pretty much the same as if we would do something like like this maybe this makes it a little bit clearer that every time we access this counter text it's actually taking this string and this integer and concatenating it which is a computation you can say and every time someone actually reads from this text this is done again however with a derived state off we can prevent this behavior so this concatenation here is just a placeholder for a very complex action so if you have some kind of complex thing that depends on states that changes over time then you want to use derived state off so let's take a look how this looks like we can say by derive state off and then simply put this stuff in here so let's let's talk about what derived state of now is and how this works what it does when it actually when counter text is accessed the first time it will compute this string so it will simply cache the string that it actually yeah concatenated here so for the first call it will be simply uh the counter is zero and that string will now be cached so the result of that computation now when the text here accesses this counter text it will get the cached version now instead so it won't recompute this concatenation which is really cool and really helpful if you actually have a very complex thing here to do that depends on states and now if actually one of these states that you use in this block changes like this counter let's say we now increase this by one then this derived set off function will recompute that string will reconquer then reconcatenate it and it will automatically notify all these different composables that use that counter text like in our case the text one here and this this scope actually as well [Music] and yeah they will actually have the new change but as long as the counter is not increased any composables that access this counter text will access the cached version and not um it actually won't recompute the string every every single time here which is yeah very simple example here won't be noticeable with a simple string but if you have some kind of complex thing you want to do that requires some states to be computed then definitely use derived state off it's an amazing function let's get to the very last effect handler we have here and that is called snapshot flow so maybe you already know the collect state function which i also quickly mentioned here in this video if you have a flow then you can use collect as state to get the flow in form of a compose state snapshot flow is the exact opposite so we can actually take a compose state and construct the flow out of that that emits values whenever the composed state changes so here for example if we have a scaffold state and the scaffold state com contains a snack bar host state and we actually want to have a flow for whatever reason that emits the snack bar message whenever it actually changes then we can actually use snapshot flow so sniper host state is a compose state and we can then simply map this to the message the snack bar shows distinct until changed just means that we only trigger this flow we only emit a new value if it actually changed so if we emit the same value as it already emitted before then it won't do anything and then we can simply collide this very simple example here that yeah you wouldn't probably use like that in practice but if you actually have some kind of composed state that you need as a flow for example to use these typical powerful flow operators then you can simply use snapshot flow and if you're actually more interested in flows in general how you can use all these powerful flow operators and even more like if you really want to be able to use flows in your project in a reactive programming style then you definitely want to use and learn with my ultimate flows playlist which you can click on here for videos and after that you're definitely ready to use flows in any kind of project
Info
Channel: Philipp Lackner
Views: 75,495
Rating: undefined out of 5
Keywords: android, tutorial, philip, philipp, filipp, filip, fillip, fillipp, phillipp, phillip, lackener, leckener, leckner, lackner, kotlin, mobile, effect handler, compose, jetpack, ui, kotlin ui, side effect, launched effect, derived state of, derivedStateOf, snapshotFlow, disposable effect, recompose, remember updated state, remember coroutine scope, produce state
Id: gxWcfz3V2QE
Channel Id: undefined
Length: 24min 56sec (1496 seconds)
Published: Sun Feb 20 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.