If that inject call is non-idempotent, it likely s...
# koin
s
If that inject call is non-idempotent, it likely should be wrapped in a lifecycle effect like this:
Copy code
@Composable
fun <T> koinInject(): T {
    val context = KoinContext.get() // this won't recompose if KoinContext is changable, if that is so this should become an Ambient
    return remember(context) { context.inject() } // inject only called once per component per context (but doesn't trigger recomposition when context changes)
}
Wanted to post it here to see what the requirements are. Code like this would make it behave similarly to a member variable.
a
intersting 👍
new contribution are discussed in #koin-dev
Could we call it just
inject
?
☝️ 1
h
Good point @Sean McQuillan [G]
I just can't understand why I'm not able to use
KoinContext
That's why I opened the github issue/feature request
I guess
inject
would be a better name as well @arnaud.giuliani. It'd follow the same names as Koin already has.
k
Also, FYI, koin breaks Compose's preview. Super surprised it does this since I was just using get() which should just be doing a hashmap lookup underneath... https://issuetracker.google.com/issues/158603732
a
not sure that Koin load/stop functions should be call inside a compose component (don’t yet what is the real impact)
I’m pretty sure we can inject things as mentioned @Sean McQuillan [G] but touching on module load ... I don’t know yet
s
This is a really good case to explore – it makes sense for this code to live in Application but we don't use that in
Preview
. One might think to call startKoin in the
Preview
, but I wouldn't recommend that today as we make no gurantees about only running one
Preview
at a time in the same AS process. 🤔
Reading the docs to try to understand the picture better, I think the current advise to put it in an
Application
onCreate
is correct even in a compose application given the global context behavior. I need to think more about trying to use this in Preview. For now my advise would be to hoist
inject
from components that you want to
@Preview
(or test) up a level:
Copy code
@Composable
fun UsesDependency() {
    val user: UserManager by inject()
    // ...
}

// transforms to

@Composable
fun DisplayUser() {
    val user: UserManager by inject()
    DisplayUser(inject())
}

@Composable
fun DisplayUser(user: UserManager) {
    // ..
}
Closing the loop here, I put a comment on that issue. This feels like we might be missing an API or execution contract for shared preview setup like this. Thanks for pointing it out!
👍 1
In a similar discussion in #compose, another refactoring came up that is worth sharing:
Copy code
@Composable
fun UsesDependency(manager: UserManager = inject()) {
   // this allows caller to bypass the service locator and inject custom values
}
Following this pattern would allow you to bypass the
inject()
call when calling from
@Preview
. Worth sharing in case it helps you with your code @kenkyee
k
Thanks Sean! I actually worked around it by passing it directly in...seems like compose was meant to work that way instead of injecting into the middle of it 🙂
s
Yes, in general formal parameters are the most "supported" path in Compose and make things work the easiest. We're still figuring out what patterns for other passing mechanisms look like and these discussions are definitely helping inform the API shape.