THIS Compose-State Mistake Leads to Problems In Your Code

Sdílet
Vložit
  • čas přidán 22. 04. 2023
  • In this video I'll show you a common Compose mistake I see people do which limits the preview and testability of your UI.
    ⭐ Get certificates for your future job
    ⭐ Save countless hours of time
    ⭐ 100% money back guarantee for 30 days
    ⭐ Become a professional Android developer now:
    pl-coding.com/premium-courses...
    Get my FREE PDF about 20 things you should never do in Jetpack Compose:
    pl-coding.com/jetpack-compose...
    💻 Let me be your mentor and become an industry-ready Android developer in 10 weeks:
    pl-coding.com/drop-table-ment...
    Join this channel to get access to perks:
    / @philipplackner
    Join my Discord server:
    / discord
    Regular programming advice on my Instagram page: / _philipplackner_
    Checkout my GitHub: github.com/philipplackner
    You like my free content? Here you can buy me a coffee:
    www.buymeacoffee.com/philippl...

Komentáře • 124

  • @Naxomiun
    @Naxomiun Před rokem +124

    You don't really need to instantiate the Viewmodel in the activity. Just do 2 composables; one as a container for the viewmodel injection and a stateless one. This way you can reuse the composables and are perfectly testables for previews and ui tests.

    • @PhilippLackner
      @PhilippLackner  Před rokem +14

      Sure that works as well 🤌🏼

    • @anegine
      @anegine Před rokem +10

      I agree this approach is better, because in this case only MainScreen knows which ViewModel it depends on.

    • @fadiselim633
      @fadiselim633 Před rokem

      Thanks for the video ! Would not the view model in this case take the activity scope ! Would not it maybe be better to have 2 composables ? I mean here U have only one screen but with multiple composables with navigations l, I think this viewmodel will live longer than u need ! Correct me if I am wrong ?

    • @jenovas00
      @jenovas00 Před rokem +5

      Wanted to say exactly the same this. I have a MainScreen where the viewModel injects normally and in it I call the MainScreenContent where I pass the state I needed and MainScreenContent is where the drawing of the UI actually happens and I preview MainScreenContent. This way you also don't break multimodule apps for example.

    • @deepakbisht4957
      @deepakbisht4957 Před rokem +1

      @@fadiselim633 yes you are wrong.
      The viewmodel that was created in the composable was a part of nav entry once it is destroyed by clicking back the viewmodel will be destroyed with that composable containing a screen level component...

  • @Narazgul
    @Narazgul Před rokem +3

    Wie gewohnt klasse erklärt und zum richtigen Zeitpunkt. Danke für deinen super content, Philipp!

  • @KamrynB
    @KamrynB Před rokem +9

    Initializing the view model in the nav graph violates the single responsibility principle. The recommended pattern is the have a stateful screen composable that wraps a stateless screen composable.

    • @adil-hussain-84
      @adil-hussain-84 Před 4 měsíci +1

      Recommended by whom? Share some links if you have some 🙏

  • @IvanVasheka
    @IvanVasheka Před rokem +6

    You can separate screen into two composables : screen and content (outer and bottom inner) and have both : viewmodel in the screen and state and working preview in content…

  • @MajinOtaku83
    @MajinOtaku83 Před rokem +1

    Something we've looked into is making a ***Route class which contains a ***Route function where we initialize the Viewmodel and any interfaces we need to allow the viewmodel to react to the screen states. We can also initialise the nav graph function in the same class, meaning it can be kept apart in its own feature nicely

  • @gregoryphiri5724
    @gregoryphiri5724 Před rokem

    It is always lovely to see your content, like lumos on our brains leaving it alight with more info. Anyway, have always been curious about the effect of state reducers. They use the copy function to create a new state which is passed to the screen itself. Does that not affect the performance of compose as the whole screen is always recomposed whenever there is even a minute state change?

  • @leonidas_30052
    @leonidas_30052 Před rokem

    Wow. After this video, I'll have to do a little update on my project kk. thanks philip

  • @the_nomadic_ajith
    @the_nomadic_ajith Před rokem

    I was working on a sample project in Compose today evening. I was thinking of how i can de-clutter my constructor.and i got that youtube notifcation from your channel like a magic. Thanks so much man!

  • @mdisi5967
    @mdisi5967 Před rokem +7

    This is very interesting I personally prefer to create a stateless container composable but what I hate about it is when you need to pass many states 😵‍💫

    • @illya_ike
      @illya_ike Před 2 měsíci

      So what is the proper solution in this case? Pass multiple states and multiple callbacks? Also some states may be needed to other composables down in the hierarchy, will you pass them all? It seems that passing viewModel is much easier. Even if it doesn't allow you to build preview. May be we should think about mocking viewModel easily without passing all dependencies to it.

  • @rayanaoufdraw2230
    @rayanaoufdraw2230 Před 3 měsíci

    thank youuuuuuuuuu you are the best my friend , you just solve my biggest problem

  • @sevbanthebuyer
    @sevbanthebuyer Před rokem +5

    Great video as always Philipp, thank you ! What i'm wondering is that are you refactoring all your old projects when you learn something new like that ?

    • @PhilippLackner
      @PhilippLackner  Před rokem +1

      Depends what you mean with old projects. I don't really have time to work on hobby projects, but in my freelance projects I do so if I have the time for that change. Sometimes other things have higher priority though

  • @mikeshilovski1512
    @mikeshilovski1512 Před 6 měsíci

    The state advice is really great! It just hit me with another idea tho. You can make an extention function above your screen composable, where you'd instantiate the view model. Then you'd just call this extension in the main activity navhost

  • @shole7035
    @shole7035 Před rokem

    thanks, I've been thinking about it recently

  • @CivoMT
    @CivoMT Před rokem +4

    Hi Philip, great video as always.
    Let's say your screen contains composables items that appear dynamically for example inside lazyColumn. Now each composable contains editable field with state which is passed to composable inside lazyColumn. How would you update state for each item individually? An obvious issue with this is approach is that if you change state at one item, the state will be updated for every item inside list.

  • @DTasmuk
    @DTasmuk Před rokem +3

    Hi Phillip,
    In your previous videos on building full-fledged apps, such as "Clean Architecture Cryptocurrency App", "CRUD Note App Using KMM" and others, you have passed hiltViewModel() right to the screens. What has changed during this time in compose/kotlin or was it bad behavior from the beginning?

  • @Alchemist10241
    @Alchemist10241 Před 10 měsíci

    extremely helpful

  • @SouravDas-jn9pk
    @SouravDas-jn9pk Před rokem +2

    Hello, how do you manage the composable if it needs more data? I mean suppose my screen needs to get data from DB, so it has to collect it from a flow object, and it needs multiple data from kotlin data store. Is this possible with a single state object?

  • @rahulhundekari1114
    @rahulhundekari1114 Před rokem +1

    Great. What if we want to listen to flow channel events as well.

  • @neronguyenvn
    @neronguyenvn Před rokem

    I hope you will make a video how to use Circuit to do state management

  • @nikolozlatsabidze2196

    I have been using MVI architecture in xml project, I can tell that there is no performance issue with that approach, even though I copy whole state after a single change. the view is not updating it's component for example text View if old and new value matches. In compose the other part of view is not even updating, this is the reason why MVI fits really well in compose, I am not sure tbh if MVI can be used in xml project, even if I like it really much, because it's really testable, understandable, there are not so many, livedata/stateFlows inside viewModel which are not repetitive, and you have effect for navigation, toast, and such kind of operations, basically 1 state and 1 event.

  • @yewo.m
    @yewo.m Před 2 měsíci

    So, in general, it's the idea that you should only pass as parameters the most minimal amount of data that the component needs to know. As a React developer myself, it's something that I've had to learn and use for a while now

  • @MobileAcademys
    @MobileAcademys Před rokem

    what about the NavController in the Composable Screen constructor as almost all on the screen need a navController

  • @the_nomadic_ajith
    @the_nomadic_ajith Před rokem +2

    You could have also show quickly how the MainEvent and MainState classes look like.

    • @deepakbisht4957
      @deepakbisht4957 Před rokem

      Lol that was not the purpose of the video...
      It has to do with the MVI architecture...

  • @D4no00
    @D4no00 Před rokem +1

    This is a classic example of why classes are not preferred over simple functions and data structures, viewmodels are a garbage can where you combine state and processing of data.

  • @madhavth
    @madhavth Před rokem +1

    Hey Philipp what do you think about passing viewmodel through local composition provider to use it throughout the screen and get the providers value on a nested composable through default params. Any thoughts?

    • @PhilippLackner
      @PhilippLackner  Před rokem +1

      Breaks the preview and has the same issue I mentioned

  • @tomlevi557
    @tomlevi557 Před 4 měsíci

    Hay , is initialize the Viewmodel on the mainActivity keep its Lifecycle Connected to the matched Screen in the Navgraph , in order that compose will match the life cycle to the screen in the navgraph and not to the main activity which alwase in the backstack?

  • @wojtekw9008
    @wojtekw9008 Před rokem +1

    There is still problem when i have to pass Channel flow events and ScaffoldState as parameters. How to resolve that? The only solution is to make that parameters nullable?

  • @mrjackson9137
    @mrjackson9137 Před 11 měsíci

    Do you have any app example of how it is done? Really struggling with this compose state passing.

  • @richardatherton5229
    @richardatherton5229 Před 9 měsíci

    I converted the CRUD notes app in your videos to this - but how do I use the navController from the viewModel?

  • @abada-s
    @abada-s Před rokem

    I hope to make a video about the state class

  • @TheMrAntosiek
    @TheMrAntosiek Před rokem

    Brilliant, make more short videos

  • @kursor4875
    @kursor4875 Před rokem

    We actually fixed this using ViewModels as interfaces (not ViewModels, but Components from Decompose to be exact). We have real implementations as well as mocks

  • @tessanix9771
    @tessanix9771 Před rokem

    But what if we have multiple child coomposables that use viewModel and we are using compositionProvider to pass ViewModel through the composable hierarchy . In this case, we will still have to use viewModel in a child composable.

  • @NarenderKumarNishad
    @NarenderKumarNishad Před rokem

    Happy Coding 😊

  • @kirwakelvinkering3122
    @kirwakelvinkering3122 Před 4 měsíci

    Hey philip , what if i pass the viewmodels via composition locals ? Would you recomment that ?

  • @bernardomesk
    @bernardomesk Před 10 měsíci

    This is exactly what you do in one of your courses, I am confused because I thought it was a good practise.

  • @nourmorgan6751
    @nourmorgan6751 Před rokem

    Hi Philipp , are you still using Compose Destinations lib ?

  • @alexandrburykin5703
    @alexandrburykin5703 Před rokem

    It's cool, but how to handle onLaunch events? Like navigation or show error?

  • @b4kuretsu
    @b4kuretsu Před měsícem

    Could you please show how the MainState class looks like? I'm new to kotlin so I would like to see the whole structure of custom states since I don't reallt get it now :P

  • @user-qw7zt5ok3g
    @user-qw7zt5ok3g Před 6 měsíci

    And if you need a value from a web service, how could you read it without pass the view model? the normal observer from the activity, seems not too convenient, let me know!. Thanks

  • @therralnoob3731
    @therralnoob3731 Před rokem

    Very Good video.
    Now a request. Could u teach us how to create Widgets with Jetpack Compose and how to make Notifications for our apps, please ? :D

  • @eriknyk2k
    @eriknyk2k Před rokem

    Could you share the repo with the code that you shown in this video please?

  • @user-lm2uf4ef6l
    @user-lm2uf4ef6l Před rokem +1

    How about setting the hiltview model as default in the main screen and creating another main screen in the form of overloading?

  • @neelancheri_yahoo
    @neelancheri_yahoo Před 2 měsíci

    I have a doubt that, Assume I have 10 compose screens in the activity. Do I need instantiate 10 viewmodels in the activity according to this pattern?

  • @CombatCat1
    @CombatCat1 Před rokem +3

    Wow, there seems to be a lot of pitfalls involved with using Compose

  • @coldkaozgaming3530
    @coldkaozgaming3530 Před rokem

    So two questions. You originally promoted DestinationsNavigator by Ramcosta. How would that work with this architecture.
    Also, this looks great on the first activity which is called from the main activity, but how do you create the next one? Surely that viewmodel would need to be created on the main page?

    • @AlexKind1917
      @AlexKind1917 Před 10 měsíci

      I think you will need to create some layer between the screen and activity. When it gets the viewModel, it maps viewModel data to the screen state and puts it to the screen component. It divides navigation and fixes the previous problem.

  • @lglf77
    @lglf77 Před rokem

    Bro! NOT is more permited recording screen ir THE Android device? I am confuse

  • @qahmed57
    @qahmed57 Před rokem

    what about sharedflow if we need to make one time action on same approach

  • @mikelantzelo
    @mikelantzelo Před rokem

    How to get channel event with this approach?

  • @nipunkumarit2168
    @nipunkumarit2168 Před rokem +7

    Hi Phillip,
    Creating this approach is beneficial for UI testing and for isolating screens. However, don't you think that instantiating the view model in the activity creates an instance of the view model that remains until the app is destroyed?
    How can we ensure that whenever the screen is decomposed, the associated view model is also freed?

    • @user-ff1fd3uh5k
      @user-ff1fd3uh5k Před rokem

      I've encountered this problem before, although in a different use case. Singleton view models are really bad unless your users have hundreds of megabytes of spare RAM on their devices, and I've solved this problem in a StackOverflow post. Idk if Philipp allows links in his comments, so you'll have to search the question by name: "Compose - get the same instance of ViewModel inside and outside of Navigation Graph"

    • @joeyeager5941
      @joeyeager5941 Před rokem +3

      you can scope the viewmodel to a specific activity/fragment/nav destination by passing the owner to the viewModelStoreOwner parameter of the viewModel() function, for example to have your viewmodel cleared out when its associated nav destination is popped off the back stack, simply pass the navBackStackEntry to the viewModelStoreOwner argument, this binding process is done automatically when you instantiate the viewModel inside your screen level composable. which is what we usually do.

    • @nipunkumarit2168
      @nipunkumarit2168 Před rokem

      @@joeyeager5941 thanks brother

    • @nipunkumarit2168
      @nipunkumarit2168 Před rokem

      @@user-ff1fd3uh5k tq

    • @akashkumardas6521
      @akashkumardas6521 Před rokem +5

      Just repeat the video until you get the answer. He instanciated the viewmodel in composable. When the composable is destroyed by using backpress or some other reason, the viewmodel will also be destroyed.

  • @khanzadakashif8248
    @khanzadakashif8248 Před rokem +1

    Which android studio version are you using? Because I am using latest version of flamingo on M1 Pro, and I have enabled Live Edit feature but still i have to rebuild every time I change anything in the composable. Otherwise it gives me that rendering problem.

    • @PhilippLackner
      @PhilippLackner  Před rokem +1

      I'm using electric eel, but preview tooling is still just trash atm

  • @ronak_kanjaria
    @ronak_kanjaria Před rokem

    Thanks for this great tip. Can you suggest how can we pass navController when we need it in the main screen. It breaks my preview

  • @johnmarkballangca5623
    @johnmarkballangca5623 Před 2 měsíci

    where the MainState come from ?

  • @xpopcornx1747
    @xpopcornx1747 Před rokem

    Not sure what is the benefit of onEvent plus sealed class instead of interface implemented by the view model with the methods. If you pass the view model in the interface parameter you don't need any of the on event switch logic. Another benefit with interface is that with composables that only need some functions then you can pass these directly.

    • @PhilippLackner
      @PhilippLackner  Před rokem

      I guess because large interfaces create much more boilerplate if you instantiate them, since you have a whole function for every single case instead of just a when branch, so they pollute your NavHost and you can't just pass viewmodel::onEvent

  • @ficc666
    @ficc666 Před rokem

    Hi Philipp,
    You mentioned MVI at the beginning, but does it matter whether it is MVI or MVVM? Thanks!

    • @darwinspace
      @darwinspace Před rokem

      He just used to say “MVI” because in the application implementation uses a ViewModel, so MVI and MVVM are comparable equals in this case

    • @ficc666
      @ficc666 Před rokem

      @@darwinspace I assumed it would be a stupid question, but thanks :)

    • @darwinspace
      @darwinspace Před rokem +2

      @@ficc666 nonono, I sorry, I’m not a native speaker, I don’t have a native sense of what my words could be offensive but I was learning programmig also a time ago.
      Stupid questions doesn’t exist, only exists stupids that doesn’t ask questions.

    • @ficc666
      @ficc666 Před rokem +1

      @@darwinspace Nah, you didn't write anything like that. It was rather a comment on my question rather than on your answer. 😎

  • @darwinspace
    @darwinspace Před rokem

    Please you can build an application with responsive design depending on the size of the screen, implementing portrait and landscape mode and implementing multiple window(portrait but half screen), I need that guide 😭

    • @deepakbisht4957
      @deepakbisht4957 Před rokem

      Lol why are you crying?

    • @darwinspace
      @darwinspace Před rokem

      @@deepakbisht4957 lol, it is not literal. I was not crying, in my country use emojis ironically

  • @FreedivingTrainer
    @FreedivingTrainer Před rokem

    There is so late for this video.. :) I have such kind of questions about year ago.. stackoverflow resolved it 😊

  • @gori_maheswari8994
    @gori_maheswari8994 Před rokem

    Amazing

  • @Mr.E.C
    @Mr.E.C Před rokem

    Why you don’t init your viewmodel with hiltviewodel inside an composable fun? Then you don‘t need to set an parameter as state.

    • @PhilippLackner
      @PhilippLackner  Před rokem +2

      Because then you have the same problem with the preview and UI tests I mentioned. Your screen composable just shouldn't know about the viewmodel it uses if you want the preview to work

    • @Mr.E.C
      @Mr.E.C Před rokem

      @@PhilippLackner I see. Thanks a lot!

  • @geekdev0
    @geekdev0 Před 8 měsíci

    Please, can you tell me what processor you have?

  • @MateuszKolbusz-mi2zm
    @MateuszKolbusz-mi2zm Před rokem

    okey, but now I can't send navigation params. There is any workaround?

  • @eduard5083
    @eduard5083 Před rokem +1

    Does anyone know where repository is? 😞

    • @theguy4084
      @theguy4084 Před rokem

      dependency can be injected by hilt..

  • @eliascanche6975
    @eliascanche6975 Před rokem

    what about the navigator?

    • @deepakbisht4957
      @deepakbisht4957 Před rokem +1

      Use DI for navigator or just place it in nav graph only. Don't pass it into screen level components...

  • @j2shoes288
    @j2shoes288 Před rokem

    I guess one issue with this is that you end up with 20 callbacks passed to the composable.

    • @PhilippLackner
      @PhilippLackner  Před rokem

      No, just one

    • @j2shoes288
      @j2shoes288 Před rokem

      @@PhilippLackner what about return values? say, viewmodel , has func1(): String, func2(): Int, how would you define the onEventHandler: (HomeScreenEvent) -> String that you pass to the composable? and then you have fun3() that has no return value, what does that return? I guess, you could have a return type Any? so, in your composable you would pass
      onEventHandler: (HomeScreenEvent) -> Any?

  • @st4849
    @st4849 Před rokem +1

    Using a master state class defeats the purpose of the scoped/optimized recomposition feature of Jetpack Compose. Only use something like this if individual fields can't change on your screen independently. Otherwise use separate states for each field so that your screen doesn't fully recompose when a single field changes.

    • @joeyeager5941
      @joeyeager5941 Před rokem

      Jetpack compose is smart enough to determine what changed and what didn't, as long as you apply state hoisting best practices and pass composables only what they need, you don't need to worry about composables being recomposed with the same state. Just use a master state class and don't worry about performance issues, compose will handle the rest.

    • @st4849
      @st4849 Před rokem

      @@joeyeager5941 Not correct. You have to use individual states inside the view model or your master class. Unlike the solution in the video.

    • @joeyeager5941
      @joeyeager5941 Před rokem

      @@st4849 Sorry, I didn't understand. What part of my answer that is incorrect?

    • @deepakbisht4957
      @deepakbisht4957 Před rokem

      Lol I think you first need to understand how exactly Composition and recomposition works and then jump into coding.
      The Android team themselves said Compose is smart enough to re-render only that part of the UI whose state changes.
      So it is better to do state hoisting and at screen level you get all the states and then breaks the state and pass into low level components by passing required data instead of passing a big state object...

    • @PhilippLackner
      @PhilippLackner  Před rokem +1

      Compose detects which fields have changed and which didn't and will only recompose what needs to be recomposed. Try it out ;)

  • @ALEX54402
    @ALEX54402 Před rokem

    Nice video ever 🥰

    • @ALEX54402
      @ALEX54402 Před rokem

      What prize sir ?

    • @ALEX54402
      @ALEX54402 Před rokem

      Just kidding 😂

    • @ALEX54402
      @ALEX54402 Před rokem

      Give the prise to next person please i am ok 😉

  • @pabloprota
    @pabloprota Před rokem

    Unfortunatelly isn't possible with Koin.

  • @SerarEstaifan
    @SerarEstaifan Před 6 měsíci

    Great video. Though all of the children composable will recompose again on every state change. Sorry, but that doesn't seem the best approach.

    • @PhilippLackner
      @PhilippLackner  Před 6 měsíci

      No they won't, compose is smart enough to detect which values changed, even when part of a data class :)

    • @SerarEstaifan
      @SerarEstaifan Před 6 měsíci

      @@PhilippLackner are you sure? When I call state.update { it.copy (value1) } assuming original state is (value1, value2). Even though I only updated value1, composable who is observing value2 will recompose as well.