THIS Flow Mistake Will Lead to Bugs in Your Code (StateFlow & Reactive Programming)

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video in this video I will talk about a common mistake I see people do when it comes to using State flow or not only state flow actually also Flows In general so very often we deal with UI state in the view model pretty much always if you're using a view model and then let's say you have something like this you have a user view model which simply contains a list of users could be for example when building a chat app and you want to display a list of users that are currently in a chat you can see a user consists of an ID an email a name and an avatar URL and then we simply have some functions you could imagine these would be called from some kind of remote API or whatever so this function is called when a user joins the chat room and this function is called when the user info is updated for example when the Avatar actually is updated so you also want to update the user in your local state and as now say you on the one hand want to show a list of all users in your UI and you also want to show the local user so whatever user is currently logged in you want to maybe show that as as a special entry on the top or so so what you will do is you will have two states one for all users and one for the local user and whenever you update the users list you will also simply just update the local user if the user ID is actually yeah the one from the local user and here correspondingly you will simply update the user info of all users and you will also update the user info of the local user if the ID is local again can you spot the mistake you can simply pause the video here for a few seconds and take a look at this code and then I will give you yeah the solution why this is an issue so let's talk about why this can be an issue here right now in this code there won't be any bug but this code is very vulnerable to bugs let's say you work in a team with some other developers or you just get back to this product after a while and you don't only have these two functions here on user joint and then when the user info updated let's say there are more infos maybe you want to use a leaves or whatever then every single time this user's list changes you will also need to remember to actually update the local user and of course the more states you have that actually derive from this users list the more complex this will be and if there's just one team and or in your team who does not know that this user this local user State actually needs to be updated every time the user's list changes because the local user could also change if this user's list changes then if they forget that you will effectively have a back new code because there is no real scenario where the local user would change but not the other users or I mean not this user's list here because the local user is always part of this user's list and as soon as you rely on this local user State and you once forget to actually update this after the user list changes then yeah you will just have a bug and your local user will not be the most up-to-date instance so what can we do instead every time you actually have a state where another state is actually derived of in composed we actually have this effect Handler derived State off but we actually can do the same with State flow we can actually totally get rid of that because we don't want to manually need to update this local user every time the user list changes so we can also get rid of this of this and now since this local user instance is always derived of this user's list we can say users dot map here we get a list of our users and we can say the local user is now users that find where the ID of the user is local and if we now say state in I will explain this in more detail after I wrote this view model scope sharing started while subscribed and now then we effectively have the same behavior as before just that this is not vulnerable to this bug I mentioned anymore so let's go through this step by step we say the local user is users.map so we say that's equal to this user's flow here and every time that changes we map the value of that user's flow so the the current users list two this value which is in the end just a user value so every time the user's value changes we just find the local user and update that in our local user stage so if we take a look here this is now also just a state flow that holds a user if we would have just done this then nothing would actually happen because we need to actually launch the flow and with state in we do that so we basically just cache the the latest value here in this local user flow we say we want to use the view model scope to launch this flow this sharing started while subscribed basically means that this code is only active while there are subscribers to this local user flow so if the UI for example is currently not subscribing to this to updates of this local user then we will also not find the local user every time the user's list changes and the initial value should be a pretty self-explanatory initially we just start with a null user since we don't have one but now effectively whenever our users list updates then our local user will automatically also update right afterwards which is exactly what we want and there can't be any weird race conditions or weird bikes that can happen this way and of course this is a simple example I want to also show you a little bit more complex examples so you might get a grasp for why you should really stick to using these flow operators together with a state in if State actually derives from from another state let's take a look at this chat view model here I prepared there's also mixed use of these users but to just give you a quick demo of what we want to do here we want to actually have this data class here this is a chat state which contains the user previews and a header title and you can imagine this is just like the screen in WhatsApp where you have your list of chats where you have all your friends um profile icons where you have the last message that they sent you or that you sent them and yeah just their name so you can actually select a chat that is the screen we kind of want to model here with the state in a very simplified way so this user preview would be just one element of that list it contains a user we want to display and the last message of that user and then we have a chat message which simply contains to a user contains a message and a time and with these two classes we effectively now want to create a state that contains a list of user previews since your user info and your chat messages might come from different places maybe from different API eyes maybe some kind of remote service or third-party service then you don't really directly get this combined list of user previews where you need to have the user info and the last message together in one state so we kind of need to map these together but we also might want to actually consider if the user is currently logged in so if the user logs out we automatically want to kind of not show the chats anymore because the user shouldn't be able to see them when they are logged out just as an example so as you can see this final Chat State here derives of quite some states in our case of these three states and whenever our is logged in changes then we want to kind of reconsider calculating our chat State whenever our messages change for example if one user actually sent a message then we want to update the corresponding user preview that the last message is now changed and whenever the user's list changes if there is a new user for example who sent you a message then we also want to update our chat state so there are now three states where our chat State actually derives off and the way we can now do this very efficiently and effectively without actually having any race conditions is using the flow combine operator so we can say Val Chat State is equal to combine and yeah as the name says that is used to combine different flows now we can combine all of them so is logged in chat messages and users and then we also get these as variables here so it's logged in then messages and users just like that so what does that now do combined will effectively call this piece of code whenever any of these three flows actually emits a new value so whenever any of these changes this block of code is now triggered with all these three values and that's actually what we want to do whenever any of these values changes we want to kind of recalculate our chat state to always reflect the most up-to-date state so if you if we wouldn't do it like this then every time this chat messages changes this logged in changes this uses changes we would also need to recalculate all these other values so if the user's list changes we only would want to update that if we are logged in so we would need to add a check if the chat messages change we would need to kind of check if the the user still exists and the users will this we will also need to check if we are still logged in and that every single time when any of these values changes so there's a lot of code involved to make this work right and especially as I said if you work in a team and your team does not know that then this will lead you backs so what we can now do in this chat state is we can first of all check if we are actually logged in because only then we want to map this to a chat State object and else we simply return not so that way we effectively already considered this is locked in Boolean because if we're not logged in then we just don't get a state out of this so let's see how we can now do this year our user previous is a list of user preview so we now need to take our messages list and our users list and kind of combine these to a user preview and we can do this using users.map and in here we would simply map each user to a user preview so we can say user preview the user is just it and the last message well we also need to kind of find this last message now given our messages list because this just contains a list of all messages of all users so we first of all need to find the message that belongs to this user and we need to find the latest message of that user to actually show it here in the last message string so what we can do is we can simply say messages that filter we only want messages where the users actually to actually equal to the let's call it current user actually this one update it here so we only want messages where the user is actually equal to the current user and we also want to consider that we get the latest message so we can say Max buy or null we want to Max this by the time so just the largest timestamp will be the latest message and then we can say that message which is the string of that message so you can see there's a lot of things going on and if we wouldn't use combine here we would need to recalculate this every single time anything changes if that like if that chat messages change if that users change we'd always need to recalculate this last message and this way it will all happen automatically thanks to using combine and we could also kind of update the header Title Here For example to I don't know the first user's name or so users that first or null that name or just chat I don't know just a simple example so you can see this is actually a lot more complex in this example but you would need to write a lot a lot a lot more code to make this work without having this reactivity thanks to combine and you would also have code that is much much more vulnerable to bugs of course right now this code wouldn't do anything because we still need to say we want to save the result of this calculation here in a flow which effectively then gets a state flow so we again want to say state in Via model scope sharing started while subscribed and the initial value is no and that way we effectively now have our chat State flow which we can then easily observe in our UI and we always can be sure that it reflects the latest UI State and if you actually realize in future that this chat State derives of more States and not only of these three ones here for example if you would also need to consider some other kind of flow and you are about to write something like that value or so to refer to the current value of the flow don't do this actually instead add the new flow here in the list and also then use the corresponding value that you get that the flow then gives you here because if you depend on the state flow's value by using here.viewmodel dot stateflow.value or so or directly in the viewmodel then this can also lead to race conditions because if we for example would get of this users list here and every time we refer to our users list we say users that value instead and here uses that value then we don't get any errors here but this can lead to race conditions because let's say the chat messages actually changes which will trigger this piece of code you will then read the current users list but while doing so the user's list actually gets updated then you actually also want to recalculate the chat state but since you you don't make this combine rely on this user's flow this wouldn't be executed so your state would be invalid because you relied on an older state of this user's list so always as soon as you have some kind of value in here that comes from a state flow make sure to include that here in combine so I hope that helped you to understand how flows work and how reactive programming works with flows to make actually use of that and get rid of some bugs in your project if so then let me know that down below and also let me know what kind of topics you might want to hear in future and want to see in future on my channel that would be awesome then I would consider doing that and if you are not a subscriber of this channel yet and you like this video then definitely do that and hit subscribe now because then you will get two Android videos every single week so you can get a better Android developer thanks for watching enjoy your week and I will see you back in the next video bye foreign
Info
Channel: Philipp Lackner
Views: 27,609
Rating: undefined out of 5
Keywords:
Id: NW03ZAQcTuY
Channel Id: undefined
Length: 13min 56sec (836 seconds)
Published: Wed Sep 21 2022
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.