THIS Compose-State Mistake Leads to Problems In Your Code

แชร์
ฝัง
  • เผยแพร่เมื่อ 11 ธ.ค. 2024

ความคิดเห็น • 128

  • @Naxomiun
    @Naxomiun ปีที่แล้ว +138

    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  ปีที่แล้ว +19

      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!

  • @cinemovieone
    @cinemovieone ปีที่แล้ว +1

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

  • @mikeshilovski1512
    @mikeshilovski1512 ปีที่แล้ว +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

  • @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 10 หลายเดือนก่อน +1

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

  • @mdisi5967
    @mdisi5967 ปีที่แล้ว +8

    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 8 หลายเดือนก่อน

      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.

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

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

  • @rayanaoufdraw2230
    @rayanaoufdraw2230 10 หลายเดือนก่อน

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

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

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

  • @shole7035
    @shole7035 ปีที่แล้ว

    thanks, I've been thinking about it recently

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

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

  • @yewo.m
    @yewo.m 8 หลายเดือนก่อน

    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

  • @PurelyAndroid
    @PurelyAndroid 2 หลายเดือนก่อน

    Great content.

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

  • @rahulhundekari1114
    @rahulhundekari1114 ปีที่แล้ว +1

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

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

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

  • @Alchemist10241
    @Alchemist10241 ปีที่แล้ว

    extremely helpful

  • @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 ปีที่แล้ว +4

      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 ปีที่แล้ว +1

      @@joeyeager5941 thanks brother

    • @nipunkumarit2168
      @nipunkumarit2168 ปีที่แล้ว

      @@CalamityDeadshot tq

    • @akashkumardas6521
      @akashkumardas6521 ปีที่แล้ว +6

      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.

  • @neronguyenvn2
    @neronguyenvn2 ปีที่แล้ว

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

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

  • @josele7666
    @josele7666 2 หลายเดือนก่อน

    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!

  • @NarenderKumarNishad
    @NarenderKumarNishad ปีที่แล้ว

    Happy Coding 😊

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

  • @abada-s
    @abada-s ปีที่แล้ว

    I hope to make a video about the state class

  • @codewithrichard
    @codewithrichard ปีที่แล้ว

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

  • @CombatCat1
    @CombatCat1 ปีที่แล้ว +3

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

  • @TheMrAntosiek
    @TheMrAntosiek ปีที่แล้ว

    Brilliant, make more short videos

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

    • @Peezzied
      @Peezzied 4 หลายเดือนก่อน

      Lame

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

  • @b4kuretsu
    @b4kuretsu 7 หลายเดือนก่อน

    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

  • @태우-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?

    • @deepakbisht4957
      @deepakbisht4957 ปีที่แล้ว

      Same thing.
      You can do both things...

  • @FreedivingTrainer
    @FreedivingTrainer ปีที่แล้ว

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

  • @mrjackson9137
    @mrjackson9137 ปีที่แล้ว

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

  • @tomlevi557
    @tomlevi557 10 หลายเดือนก่อน

    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?

  • @mikelantzelo
    @mikelantzelo ปีที่แล้ว

    How to get channel event with this approach?

  • @richardatherton5229
    @richardatherton5229 ปีที่แล้ว

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

  • @FlaviaEstefaniaFigueroaCastill
    @FlaviaEstefaniaFigueroaCastill ปีที่แล้ว

    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

  • @johnmarkballangca5623
    @johnmarkballangca5623 8 หลายเดือนก่อน

    where the MainState come from ?

  • @nourmorgan6751
    @nourmorgan6751 ปีที่แล้ว

    Hi Philipp , are you still using Compose Destinations lib ?

  • @alexandrburykin5703
    @alexandrburykin5703 ปีที่แล้ว

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

  • @eriknyk2k
    @eriknyk2k ปีที่แล้ว

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

  • @qahmed57
    @qahmed57 ปีที่แล้ว

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

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

  • @kirwakelvinkering3122
    @kirwakelvinkering3122 10 หลายเดือนก่อน

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

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

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

  • @neelancheri_yahoo
    @neelancheri_yahoo 9 หลายเดือนก่อน

    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?

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

  • @eduard5083
    @eduard5083 ปีที่แล้ว +1

    Does anyone know where repository is? 😞

    • @theguy4084
      @theguy4084 ปีที่แล้ว

      dependency can be injected by hilt..

  • @lglf77
    @lglf77 ปีที่แล้ว

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

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

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

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

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

    • @PhilippLackner
      @PhilippLackner  ปีที่แล้ว

      Don't pass it and use callbacks instead

  • @geekdev0
    @geekdev0 ปีที่แล้ว

    Please, can you tell me what processor you have?

  • @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 😉

  • @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 ;)

  • @MateuszKolbusz-mi2zm
    @MateuszKolbusz-mi2zm ปีที่แล้ว

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

    • @PhilippLackner
      @PhilippLackner  ปีที่แล้ว

      I don't see why you can't pass them

  • @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!

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

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

  • @gori_maheswari8994
    @gori_maheswari8994 ปีที่แล้ว

    Amazing

  • @pabloprota
    @pabloprota ปีที่แล้ว

    Unfortunatelly isn't possible with Koin.

  • @SerarEstaifan
    @SerarEstaifan ปีที่แล้ว

    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  ปีที่แล้ว

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

    • @SerarEstaifan
      @SerarEstaifan ปีที่แล้ว

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