Sealed Classes for UI State are an ANTI-PATTERN - Here's why!

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new quick video in which I want to talk about why I don't recommend using sealed classes for UI State at least if you structure it like this so let's say you have a social network app with a profile screen and the user profile could be either from the local user or from a remote user and depending on what kind of users profile you are on you just have some differing properties in your state so you can see on the one hand we have some shared properties like if we're loading the users posts and the posts themselves that's what we need to display for both the local and a remote user but if we are on a remote users profile then we might additionally want to show whether we are following that user or not but that is something we wouldn't want to show for the local user because we can follow ourselves and the same way the local user might have some different State Properties like um whether they are updating their profile picture that is something we of course can't do for remote users but only for our own profile so it might sound like a good way to structure your state like this that you have a SE class you have your shared properties that are common between all of profile screens and then you have different options depending on what type of screen you're on and in the view model this might then look like this you have some kind of state compos State and let's say the initial state is our local user state looks clean doesn't it yes but only at the first glance let's now see what actually happens if we want to toggle whether we are following that user or not that's something that is only doable on remote users profiles and because of that we would first need to check if we are on a remote users profile so we need to check if our state is of instance profile state. remote user and then we need to copy that state to change the is following value to whatever want to change it to in this case want to toggle it so what we need to do is we need to first of all check if our state is um a local a remote user state so we try to cast it as a profile State remote user which will return now if it's not a remote user and otherwise it won't then we need a let block to get null safe access on this remote user or remote State instance and then we can say we update our state so we say uh actually state is remote state. copy and is following is equal to remote state is following negated and if our state is a local user State then this would return null we would never jump inside of this Le block and not do anything which is exactly what we would expect in this scenario so far so good but what happens now if you want to change let's say the is loading Boolean whether we're currently loading the post or not that's something that we would like to toggle on both of these types of screens on local and remote user screens so if we now have a function to toggle loading or so then what I would expect is that we can just say state is equal to State copy this loading is equal to state is loading but that doesn't work because our state function does not have a copy function why is that well because our state is a sealed class and only data classes have this copy function implemented by default so in order to change a shared property here we would still need to check for the corresponding type so what we would actually need is we would need a when expression here check what type of instance this state is actually of and then say state. copy is loading oh okay there is actually no is loading value why is that well because we did not pass these properties down to our remote user so what we could do is we could make these open vals so we can override them in our sub classes we could then go ahead in here say override Val is loading which is initially false and do the same for posts which is a list of post by default an empty list we take this and do the same for the local user and then we have our loading booing here which we could toggle to state is loading we then do the same here for our remote user just the same code because we can only have this copy function for actual data classes and the state is not a data class but the local and remote user itself is but you notice that we still get an error here it says smart cast to profile State local user is impossible because state is a mutable property that could have been changed by this time what the heck does that mean well normally with cotton we have smart casting so when we check for a certain type then we we know for sure that everything that follows here inside of that check in that case the type of our state instance must be a local user but that does not work if our state is actually a global property inside of this class since there could be a multi-threading environment here where the state property is changed after we make the check for a local user so right before going inside of this copy Cod the state could have already been changed to a remote user State and then we wouldn't have this this type SA anymore if the state would be initialized in this function that would all not be a problem because then um the ID is smart enough to recognize that there are no threats in between that could change this uh state but if it's Global then there could be any other threat inside of this class that changes the state in between that is why we need an additional type check here so we would actually need to say State and cast that as a local user State again have the let block like before and then say it. copy is loading is it. is loading now we have another problem because the result of this could potentially be null if the cast fails but our state instance actually expects a non-nullable value so we could of course remove this question mark here then that would get rid of the issue but if our state would have been changed then this code would crash because the the cast would fail so you can see how big of a pain it now becomes to update these simple share properties of our state which is why I absolutely can't recommend this approach another thing that is terrible about that is that we need to duplicate all these properties here for every single state just in order to change these since in order to use the copy function with these properties they just need to be part of the data class itself so let's talk about a better alternative in the end we want an easy way to update the shared properties just by saying state is equal to state. copy and we just want to need those type casts if we're changing a property that is very specific to one of these top of users so why don't we just establish that sealed hierarchy just for those individual properties so we could have something like a sealed interface profile details for example and we say okay we have a data class for local users where the individual property is is updating profile picture that is of type profile details and we have the same for remote users where the property is is follow and now we can get rid of the state as it is here make that a data class instead we'll leave our shared properties just normal properties of this data class and then just have an additional details property here which we could also set to some kind of default value here so profile details local for example and say okay initially we're not updating the profile picture if we do it like that and we want to toggle a Shar property we can very easily do this by just saying state is equal to state. copy we now have the copy function since our state itself is now a data class we say is loading is state is loading that works perfectly fine and if we want to toggle a specific property we would need to take the details reference cast that as a profile details. remote and then get our details reference here and we say our actual state is equal to state. copy details is details. copy and here we can then change the following to details. is following still a little bit annoying with these type casts but somehow we need to differentiate between our different screens if we model it like this so now something like this is only needed when we want to change either the is updating profile picture or is following Boolean or other ones if we would extend this but not anymore for the common properties is loading and posts that's at least one way to model this in a great way another approach would be just modeling the hierarchy with the view model itself so you might have a profile view model which is an abstract class which has a state that only hosts those shared properties and that just offers the functionality to fetch posts to change the loading state so basically just all the functionality and logic that is needed for both local and remote users profiles and then you could have individual sub viw models that inherit from this parent profile view model one for the local screen and one for the remote screen which only implements the the functionality specific to to each of these screens that is also a very viable approach here but please avoid these sealed class States at least when the seal class is the state instance itself having a SE hierarchy inside of the state itself like for the profile details absolutely fine if these properties are actually individual for each single case so I hope I could help someone here if you want more such tips regarding UI building regarding jetpack compose then check out my free PDF down below where I talk about 20 really deadly mistakes you can make with compost which you probably not even aware of so click the link Down Below download that PDF and other than that thanks so much for watching this video I will see you back in the next one have an amazing rest of your week bye-bye
Info
Channel: Philipp Lackner
Views: 19,674
Rating: undefined out of 5
Keywords:
Id: NG0PPt-CaYE
Channel Id: undefined
Length: 9min 52sec (592 seconds)
Published: Sun Apr 07 2024
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.