THIS Compose-State Mistake Leads to Problems In Your Code

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new video in this video I want to show you a very common mistake I see people do when they stick to the MBI architecture in Jetpack compose projects so something I see all the time is that people have such a screen composable they inject their view model in it as a parameter then refer to the screen state which is simply just a state class here that combined combines your whole screen's State and then in the screen itself they refer to these State fields and also send actions to the view model in form of such events which are just a seal interface in this case so far so good let's get straight to the point what is the problem with this and there are two major problems with this approach which are both very easy to fix and doesn't require any big restructuring but you will have to do that in the end problem number one is this is very bad for making the preview work for your whole screen because if we would create a preview here um what is it called main screen preview we can have our compose view model State theme and then put in our main screen here if we take a look in our design tab or just split tab and then rebuild we can see we get render issues why is that well because our preview does not know how to create our view model so if we take a look in our review model we see okay this view model here is actually taking in an instance of the repository and of course if we just assume that Hilt will properly inject this viewmodel then this will work for our real code in our real app if we launch it but not for the preview because how should this preview know how our repository looks like it can't know that so what we would need to do here in order to make our preview work is we would need to pass a custom view model just for the preview so we would have a view model here and now we need a repository so we would need to pass in a custom repository just for this custom view model we might need to construct your actual repository or create a fake one which might need other dependencies and it's also very common that your v-model does not just need a repository instance but has many more fields that are injected in the Constructor which would all need to be passed for a simple preview we just want to see how your UI looks the simple looks of your UI are actually only dependent on the state but not on your whole view model so this can be a good solution another problem this current approach brings is that we can't test our main screen in isolation using a new eye test so testing something in isolation just means that we really just take a look at this specific main screen and ignore all outside factors that could have an impact on how this looks like one of these outside factors would be overview model here so an isolated UI test in this case would for example just check hey if our field one is this specific value is this value really the text of the text composable and can we find this text with a value in our screen on the other hand we have integration tests which are also very common and also make a lot of sense where you might want to test how your UI interacts together with your real model in that case you of course need a specific view model for testing but not for these isolated tests but what I'm trying to say is that also for your isolated UI tests you would need to provide a cast custom instance of your view model which you don't even need for these tests so here if you take a look in Android tests and I have a sample test class where we set the content of our UI test to main screen this would still take in an instance of our main view model and we would need to provide our custom instance here because otherwise the UI test won't know how our viewmodel is constructed and even if it would know that we wouldn't be able to test the screen in isolation because it would always interact with our view model so let's finally see how we can fix this and this fix is actually fairly easy but you have to apply it of course first of all in your screen composables don't pause the viewmodel keep your composables viewer model free and instead you want to pass the state class here so or main State and we can then also get rid of this line because in the anti-screen doesn't need to know anything else other than your state to display all its UI components but also you're seeing that we also want to send events from the screen to a review model and because we don't now don't have the HB model instance anymore we can't execute this on event function of our review model for that we can very easily just pass in a Lambda here called on event and we send main events in this Lambda function and then we can simply get rid of this view model here and we just call our Lambda but at some point we have to initialize our view model right yes of course and we want to do that instead in our main activity where our nav host is so in this composable block we can now safely instantiate our viewmodel and get the state of it so we can say we have our v-model is equal to Field view model of type main view model and then we say we have our state which is equal to which is by view model State collect collect oh I'm actually not even using a flow here so we can just set it to viewmodel.state I'm just using compose States here for the sake of Simplicity and then all we need to do is we need to take the screen composable and pass in our state and for on event we simply say viewmodel double colon on event and we just delegate all these UI events we sent from the screen composable to our review model which we have access to here now the big advantage of this approach is that if we take a look in the preview we of course now need to provide a state but the state is really everything we need in order to see a preview so we can now pass in a custom State and for example we can see we can say okay what happens if the field 2 is actually hello world and we'll leave the other fields at the default and then we can also say on event actually here on event is equal to an empty Lambda because we don't care about these events and now we actually see a preview because now it's working and we can very easily now change these fields for the preview so we could also change our field one which is an integer here choose something like 15 or 156 and then we will immediately see that in the preview or almost immediately here as you can see so that is how you can now very easily see your whole screen as a preview in isolation without actually needing to instantiate the view model for that also and getting back to UI testing this is now also much easier because we could now just set up our main screen for this specific UI test with a specific State instance for example a main state that has okay or field two is or field yeah field two is hello world and then the test Oh Let's ignore the events like this and then the test would simply just see if it can find a text composable on the screen that has hello world it could also be a UI test that for example checks okay if the is loading field I think I already have that yet if this loading field is true that you actually see an overlay or a dialog on the screen with a loading spinner that could also be be a UI test for that you really don't need a view model to check if the UI properly displays that if the state is true and of course this only works well if you stick to this type of architecture where you have one state class per screen and use these UI events which is commonly done in mvi with other types of architecture this might not be as easy as that but in my experience I have not found a single downside of this approach of using this type of architecture for composed projects because having a single screen State just uh yeah synergizes so well with compose since only those composables that use a field that changed will recompose and not the other ones so I hope you found this video helpful if you did then you will definitely also find my more advanced Android premium courses helpful which you can all find using the first link in this video's description apart from that I wish you an amazing rest of your week and I will see you back in the next video bye bye foreign
Info
Channel: Philipp Lackner
Views: 39,265
Rating: undefined out of 5
Keywords:
Id: 4D79It7Jnzg
Channel Id: undefined
Length: 7min 58sec (478 seconds)
Published: Sun Apr 23 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.