THIS Compose-State Mistake Leads to Problems In Your Code

Поделиться
HTML-код
  • Опубликовано: 19 окт 2024

Комментарии • 128

  • @Naxomiun
    @Naxomiun Год назад +135

    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  Год назад +18

      Sure that works as well 🤌🏼

    • @anegine
      @anegine Год назад +11

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

    • @fadiselim633
      @fadiselim633 Год назад

      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 Год назад +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 Год назад +2

      @@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...

  • @IvanVasheka
    @IvanVasheka Год назад +7

    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…

  • @Narazgul
    @Narazgul Год назад +4

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

  • @the_nomadic_ajith
    @the_nomadic_ajith Год назад +2

    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!

  • @mikeshilovski1512
    @mikeshilovski1512 10 месяцев назад +1

    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

  • @mdisi5967
    @mdisi5967 Год назад +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 6 месяцев назад

      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.

  • @KamrynB
    @KamrynB Год назад +10

    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 8 месяцев назад +1

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

  • @cinemovieone
    @cinemovieone Год назад +1

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

  • @MajinOtaku83
    @MajinOtaku83 Год назад +2

    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

  • @sevbanthebuyer
    @sevbanthebuyer Год назад +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  Год назад +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

  • @CivoMT
    @CivoMT Год назад +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.

  • @bernardomesk
    @bernardomesk Год назад +1

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

  • @DTasmuk
    @DTasmuk Год назад +4

    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?

  • @rayanaoufdraw2230
    @rayanaoufdraw2230 8 месяцев назад

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

  • @yewo.m
    @yewo.m 6 месяцев назад

    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

  • @kursor4875
    @kursor4875 Год назад

    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

  • @gregoryphiri5724
    @gregoryphiri5724 Год назад

    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?

  • @the_nomadic_ajith
    @the_nomadic_ajith Год назад +2

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

    • @deepakbisht4957
      @deepakbisht4957 Год назад

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

  • @josele7666
    @josele7666 10 дней назад

    Other question about this.
    And in that case, how am I listening in the composable to set possible changes with the collectasstatewithlifecycle? Thank you so much!

  • @SouravDas-jn9pk
    @SouravDas-jn9pk Год назад +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?

  • @nipunkumarit2168
    @nipunkumarit2168 Год назад +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?

    • @CalamityDeadshot
      @CalamityDeadshot Год назад

      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 Год назад +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 Год назад

      @@joeyeager5941 thanks brother

    • @nipunkumarit2168
      @nipunkumarit2168 Год назад

      @@CalamityDeadshot tq

    • @akashkumardas6521
      @akashkumardas6521 Год назад +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.

  • @rahulhundekari1114
    @rahulhundekari1114 Год назад +1

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

  • @nikolozlatsabidze2196
    @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.

  • @wojtekw9008
    @wojtekw9008 Год назад +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?

  • @shole7035
    @shole7035 Год назад

    thanks, I've been thinking about it recently

  • @neronguyenvn
    @neronguyenvn Год назад

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

  • @b4kuretsu
    @b4kuretsu 6 месяцев назад

    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

  • @neelancheri_yahoo
    @neelancheri_yahoo 7 месяцев назад

    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 Год назад +3

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

  • @MobileAcademys
    @MobileAcademys Год назад

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

  • @tessanix9771
    @tessanix9771 Год назад

    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.

  • @johnmarkballangca5623
    @johnmarkballangca5623 6 месяцев назад

    where the MainState come from ?

  • @alexandrburykin5703
    @alexandrburykin5703 Год назад

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

  • @FlaviaEstefaniaFigueroaCastill
    @FlaviaEstefaniaFigueroaCastill 10 месяцев назад

    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

  • @nourmorgan6751
    @nourmorgan6751 Год назад

    Hi Philipp , are you still using Compose Destinations lib ?

  • @tomlevi557
    @tomlevi557 8 месяцев назад

    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?

  • @abada-s
    @abada-s Год назад

    I hope to make a video about the state class

  • @태우-n4e
    @태우-n4e Год назад +1

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

  • @mrjackson9137
    @mrjackson9137 Год назад

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

  • @kirwakelvinkering3122
    @kirwakelvinkering3122 8 месяцев назад

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

  • @PurelyAndroid
    @PurelyAndroid 15 дней назад

    Great content.

  • @richardatherton5229
    @richardatherton5229 Год назад

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

  • @khanzadakashif8248
    @khanzadakashif8248 Год назад +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  Год назад +1

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

  • @lglf77
    @lglf77 Год назад

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

  • @D4no00
    @D4no00 Год назад +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.

  • @qahmed57
    @qahmed57 Год назад

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

  • @mikelantzelo
    @mikelantzelo Год назад

    How to get channel event with this approach?

  • @darwinspace
    @darwinspace Год назад

    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 Год назад

      Lol why are you crying?

    • @darwinspace
      @darwinspace Год назад

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

  • @madhavth
    @madhavth Год назад +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  Год назад +1

      Breaks the preview and has the same issue I mentioned

  • @therralnoob3731
    @therralnoob3731 Год назад

    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

  • @coldkaozgaming3530
    @coldkaozgaming3530 Год назад

    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 Год назад

      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.

  • @NarenderKumarNishad
    @NarenderKumarNishad Год назад

    Happy Coding 😊

  • @xpopcornx1747
    @xpopcornx1747 Год назад

    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  Год назад

      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

  • @eriknyk2k
    @eriknyk2k Год назад

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

  • @Alchemist10241
    @Alchemist10241 Год назад

    extremely helpful

  • @ronak_kanjaria
    @ronak_kanjaria Год назад

    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

  • @eduard5083
    @eduard5083 Год назад +1

    Does anyone know where repository is? 😞

    • @theguy4084
      @theguy4084 Год назад

      dependency can be injected by hilt..

  • @TheMrAntosiek
    @TheMrAntosiek Год назад

    Brilliant, make more short videos

  • @j2shoes288
    @j2shoes288 Год назад

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

    • @PhilippLackner
      @PhilippLackner  Год назад

      No, just one

    • @j2shoes288
      @j2shoes288 Год назад

      @@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?

  • @FreedivingTrainer
    @FreedivingTrainer Год назад

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

  • @MateuszKolbusz-mi2zm
    @MateuszKolbusz-mi2zm Год назад

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

  • @geekdev0
    @geekdev0 Год назад

    Please, can you tell me what processor you have?

  • @ficc666
    @ficc666 Год назад

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

    • @darwinspace
      @darwinspace Год назад

      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 Год назад

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

    • @darwinspace
      @darwinspace Год назад +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 Год назад +1

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

  • @eliascanche6975
    @eliascanche6975 Год назад

    what about the navigator?

    • @deepakbisht4957
      @deepakbisht4957 Год назад +1

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

  • @Mr.E.C
    @Mr.E.C Год назад

    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  Год назад +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 Год назад

      @@PhilippLackner I see. Thanks a lot!

  • @st4849
    @st4849 Год назад +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 Год назад

      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 Год назад

      @@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 Год назад

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

    • @deepakbisht4957
      @deepakbisht4957 Год назад

      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  Год назад +1

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

  • @pabloprota
    @pabloprota Год назад

    Unfortunatelly isn't possible with Koin.

  • @gori_maheswari8994
    @gori_maheswari8994 Год назад

    Amazing

  • @ALEX54402
    @ALEX54402 Год назад

    Nice video ever 🥰

    • @ALEX54402
      @ALEX54402 Год назад

      What prize sir ?

    • @ALEX54402
      @ALEX54402 Год назад

      Just kidding 😂

    • @ALEX54402
      @ALEX54402 Год назад

      Give the prise to next person please i am ok 😉

  • @SerarEstaifan
    @SerarEstaifan 10 месяцев назад

    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  10 месяцев назад

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

    • @SerarEstaifan
      @SerarEstaifan 10 месяцев назад

      @@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.