Jetpack Compose LaunchedEffect confusing - kotlin

LaunchedEffect changes in value is the implementation of println
But why it is also executed when entering the default value for the first time, it is really confusing, why is it designed like this.
LaunchedEffect(value) {
println("--1--")
}
How to avoid first execution?

LaunchedEffect starts when the Composable enters the composition and restarts when the key(s) is changed. You can't avoid executing it, but inside you can check if it's the default value, meaning this is the first run.
LaunchedEffect(value) {
if (value != defaultValue) println("--1--")
}

How to avoid first execution
If you want to avoid only first execution, you wrap your LaunchedEffect(Unit) in an if{} block - if this is what you want.
if(yourCondition){
LaunchedEffect(){
}
}
LaunchedEffect triggers
during initial composition or if it's key changes
during initial composition, which also included adding it in view-tree conditionally, even in case of re-composition.
For eg -> If you first if condition during composition added the LaunchedEffect in the view-tree, and if removed in further re-composition and added again (it will again be re-launched).

Related

What causes Vue 2 to check a "get" function/property?

My recent work in Vue (we're still using Vue 2 unfortunately) has caused me to question my understanding of how Vue checks property values and re-renders.
I've got a couple of components on my page which have a v-show clause tied to a get statement in the code:
<my-component v-show="this.isRequired">
public get isRequired(): boolean {
if(this.model.myBooleanProperty == true && this.model.myNumberProperty > 0) {
return true;
}
if(this.model.myOtherBooleanProperty == true && this.model.myOtherNumberProperty > 0) {
return true;
}
return false;
}
Now, my understanding is that Vue would check this function whenever any of the involved properties changed. So if the values of any out of myBooleanProperty, myNumberProperty, myOtherBooleanProperty and myOtherNumberProperty changed then isRequired would be checked and the v-show clause would cause the component to show or not show depending on the outcome.
However, I've learned this isn't the case. By commenting out parts of the function, it seems that only changes to myBooleanProperty, myNumberProperty, myOtherBooleanProperty ever cause isRequired to be checked, even if they're taken out of the function. myOtherNumberProperty never causes it to be checked, even if it's directly manipulated in isRequired by setting it to zero or null.
Can someone please explain what, under these circumstances, causes Vue to reevaluate the value of isRequired?
Don't use this in the template, it's not necessary.
I understand you use vue-property-decorator library, right? Cause a get x property is a class getter, which is compiled down to a Vue computed property.
You are right, computed properties react and reevaluate whenever one of their reactive dependency updates. myOtherNumberProperty should also trigger the computed to reevaluate its value.
Maybe you have a reactivity problem with this property. Check that this value is properly initialized in your data function. If it's missing in the initial model object, Vue won't make it reactive and thus, its changes won't trigger anything.
I can't help further more without additional context on your code.

Unable to assert state flow value in view model

The view model is given below
class ClickRowViewModel #Inject constructor(
private val clickRowRepository: ClickRowRepository
): ViewModel() {
private val _clickRowsFlow = MutableStateFlow<List<ClickRow>>(mutableListOf())
val clickRowsFlow = _clickRowsFlow.asStateFlow()
fun fetchAndInitialiseClickRows() {
viewModelScope.launch {
_clickRowsFlow.update {
clickRowRepository.fetchClickRows()
}
}
}
}
My test is as follows:
I am using InstantTaskExecutorRule as follows
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
The actual value never resolves to the expected value even though $result seems to have two elements but the actualValue is an empty list. I don't know what I am doing wrong.
Update
I tried to use the first terminal operator as well but the returned output returns an empty list.
Update # 2
I tried async but I got the following error
kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing, there were active child jobs: [DeferredCoroutine{Active}#a4a38f0]
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$3$3.invokeSuspend(TestBuilders.kt:342)
Update # 3
This test passes in Android Studio, but fails using CLI
Test failing in CLI
You can't call toList on a SharedFlow like that:
Shared flow never completes. A call to Flow.collect on a shared flow never completes normally, and neither does a coroutine started by the Flow.launchIn function.
So calling toList will hang forever, because the flow never hits an end point where it says "ok that's all the elements", and toList needs to return a final value. Since StateFlow only contains one element at a time anyway, and you're not collecting over a period of time, you probably just want take(1).toList().
Or use first() if you don't want the wrapping list, which it seems you don't - each element in the StateFlow is a List<ClickRow>, which is what clickRowRepository.fetchClickRows() returns too. So expectedValue is a List<ClickRow>, whereas actualValue is a List<List<ClickRow>> - so they wouldn't match anyway!
edit your update (using first()) has a couple of issues.
First of all, the clickRowsFlow StateFlow in your ViewModel only updates when you call fetchAndInitialiseClickRows(), because that's what fetches a value and sets it on the StateFlow. You're not calling that in your second example, so it won't update.
Second, that StateFlow is going to go through two state values, right? The first is the initial empty list, the second is the row contents you get back from the repo. So when you access that StateFlow, it either needs to be after the update has happened, or (better) you need to ignore the first state and only return the second one:
val actualValue = clickRowViewModel.clickRowsFlow
.drop(1) // ignore the initial state
.first() // then take the first result after that
// start the update -after- setting up the flow collection,
// so there's no race condition to worry about
clickRowsViewModel.fetchAndInitialiseClickRows()
This way, you subscribe to the StateFlow and immediately get (and drop) the initial state. Then when the update happens, it should push another value to the subscriber, which takes that first new value as its final result.
But there's another complication - because fetchAndInitialiseClickRows() kicks off its own coroutine and returns immediately, that means the fetch-and-update task is running asynchronously. You need to give it time to finish, before you start asserting any results from it.
One option is to start the coroutine and then block waiting for the result to show up:
// start the update
clickRowsViewModel.fetchAndInitialiseClickRows()
// run the collection as a blocking operation, which completes when you get
// that second result
val actualValue = clickRowViewModel.clickRowsFlow
.drop(1)
.first()
This works so long as fetchAndInitialiseClickRows doesn't complete immediately. That consumer chain up there requires at least two items to be produced while it's subscribed - if it never gets to see the initial state, it'll hang waiting for that second (really a third) value that's never coming. This introduces a race condition and even if it's "probably fine in practice" it still makes the test brittle.
Your other option is to subscribe first, using a coroutine so that execution can continue, and then start the update - that way the subscriber can see the initial state, and then the update that arrives later:
// async is like launch, but it returns a `Deferred` that produces a result later
val actualValue = async {
clickRowViewModel.clickRowsFlow
.drop(1)
.first()
}
// now you can start the update
clickRowsViewModel.fetchAndInitialiseClickRows()
// then use `await` to block until the result is available
assertEquals(expected, actualValue.await())
You always need to make sure you handle waiting on your coroutines, otherwise the test could finish early (i.e. you do your asserting before the results are in). Like in your first example, you're launching a coroutine to populate your list, but not ensuring that has time to complete before you check the list's contents.
In that case you'd have to do something like advanceUntilIdle() - have a look at this section on testing coroutines, it shows you some ways to wait on them. This might also work for the one you're launching with fetchAndInitialiseClickRows (since it says it waits for other coroutines on the scheduler, not the same scope) but I'm not really familiar with it, you could look into it if you like!

How to wait for Room to update?

I'm trying to make a function that first updates a model field in a Database, then imediatly retrives this same model. I'd like to get that model with the udpated field. I'm trying this:
var snack = appViewModel.getSnack(snackId)
runBlocking { appViewModel.updateSnack(snack.copy(total = value)) }
snack = appViewModel.getSnack(snackId)
However, even when using runBlocking, snack ends up being with the old total value. I don't know how Room suspending functions for #Update are implemented, but it seems that even with runBlocking this runs assyncrhronaly, so the field doesn't get updated in time. How do I proceed?
As #Tenfour04 pointed out, my implementation in the ViewModel was launching a viewModelScope, so the function returned imediately, and runBlocking{} was useless. As a temporary measure, I just added .join() to the runBlocking call, so it waits for the Job to finish:
runBlocking { appViewModel.updateSnack(snack.copy(total = value)).join() }
As #Tenfour04 also pointed out, my design is flawed, because we shoudn't be blocking the UI. I'm now trying to implement everything with Flow<> and collectAsState().

FreeMarker ?has_content on Iterator

How does FreeMarker implement .iterator()?has_content for Iterator ?
Does it attempt to get the first item to decide whether to render,
and does it keep it for the iteration? Or does it start another iteration?
I have found this
static boolean isEmpty(TemplateModel model) throws TemplateModelException
{
if (model instanceof BeanModel) {
...
} else if (model instanceof TemplateCollectionModel) {
return !((TemplateCollectionModel) model).iterator().hasNext();
...
}
}
But I am not sure what Freemarker wraps the Iterator type to.
Is it TemplateCollectionModel?
It doesn't get the first item, it just calls hasNext(), and yes, with the default ObjectWrapper (see the object_wrapper configuration setting) it will be treated as a TemplateCollectionModel, which can be listed only once though. But prior to 2.3.24 (it's unreleased when I write this), there are some glitches to look out for (see below). Also if you are using pure BeansWrapper instead of the default ObjectWrapper, there's a glitch (see below too).
Glitch (2.3.23 and earlier): If you are using the default ObjectWrapper (you should), and the Iterator is not re-get for the subsequent listing, that is, the same already wrapped value is reused, then it will freak out telling that an Iterator can be listed only once.
Glitch 2, with pure BeansWrapper: It will just always say that it has content, even if the Iterator is empty. That's also fixed in 2.3.24, however, you need to create the BeansWrapper itself (i.e., not (just) the Configuration) with 2.3.24 incompatibleImprovements for the fix to be active.
Note that <#list ...>...<#else>...</#list> has been and is working in all cases (even before 2.3.24).
Last not least, thanks for bringing this topic to my attention. I have fixed these in 2.3.24 because of that. (Nightly can be built from here: https://github.com/apache/incubator-freemarker/tree/2.3-gae)

Iteration-scoped variable in JSF / Richfaces iteration?

OK, this should be an interesting one I think. I want to minimize the number of invocations to my Seam component inside an iteration. I am using Seam 2.2.1.CR1, Richfaces 3.3.3.Final, JSF 1.2 and Facelets.
Please take a look the following Facelet snippet:
<rich:datatable value="#{myBean.products}" var="prod">
<rich:column rowspan="#{rowspan.calcHomePageProductRowspan(prod)}">
#{prod.name}
</rich:column>
<rich:column rowspan="#{rowspan.calcHomePageProductRowspan(prod)}">
#{prod.number}
</rich:column>
...
<rich:column rowspan="#{rowspan.calcHomePageProductRowspan(prod)}">
#{prod.somethingElse1}
</rich:column>
<rich:column rowspan="#{rowspan.calcHomePageProductRowspan(prod)}">
#{prod.somethingElse2}
</rich:column>
...
<rich:column rowspan="#{rowspan.calcHomePageProductRowspan(prod)}">
#{prod.somethingElse3}
</rich:column>
</rich:datatable>
In the above code I am computing an attribute (here the rowspan, but that doesn't really matter, it could be any attribute or value at all, so please don't focus on that) by evaluating an EL expression. As you can see, the method that calculates the value takes the current prod as an argument.
I have made an internal optimization in the rowspan Seam component, and it keeps in a HashMap all the already computed values for products. So, when the the EL expression is evaluated at the second rich:column, the rowspan first looks up in the HashMap the already computed value, and returns that, instead of re-computing all over again.
Although this is better that re-computing all over again, I still have to make an invocation to a Seam component. Is there a way to somehow invoke the Seam component only once, and somehow retain the computed value for the current iteration?
The analogous to Java would be to define a variable inside the loop at each iteration, and reuse it throughout the iteration.
Note: I have already made other Seam-oriented optimizations such as #BypassInterceptors, the Seam component is in the EVENT scope, so no hierarchical lookups take place etc.
Is there a way to somehow invoke the Seam component only once, and somehow retain the computed value for the current iteration?
Sure, in theory.
But I am not sure I fully understand your question. Which seam component are you talking about? rowspan?
If that is the case, then yeah, its invoked each time you call it, which makes sense. You are looping through a dataTable, and for each row you call it.
Without knowing more details about what you are trying to do, its difficult to suggest an answer. Is the code slow? Is that why you need to optimize further?
Update
Try this, though I am not sure if it works
<rich:dataTable value="#{myBean.products}" var="prod">
<ui:param name="myrowspan" value="#{rowspan.calcHomePageProductRowspan(prod)}"/>
<rich:column rowspan="#{myrowspan}">
#{prod.name}
</rich:column>
</rich:dataTable>
Second update
So if you don't change your code to be a #Out, #Factory, #Unwrap or similar, then this will always be evaluated each time it runs. This is just how JSF works.
That's why they say that you should do this in your getters, because JSF will call this for each JSF Phase.
public List<Foo> getFoo() {
if(this.field != null) { 
field = (List)entityManager.createQuery("from Foo").getResultList();
}
return this.field;
}
If you wouldn't have cached the list, and checking for null, JSF would hit the database for each phase and for each row in the data table.
Thus this is just something you have to live with.