Ability to modify state in Signal.effect
Describe your motivation
In some cases, when you receive a signal effect, you want to update another value based on the new value. But Signal does not allow you to do it, as it throws Cannot make changes in a read-only transaction.
Describe the solution you'd like
I understand that when you allow that, the code would get stuck in an infinite loop, but there should be a way. Maybe Signal.invokeSilently(), or something like that.
Describe alternatives you've considered
Another solution would be Signal.effect would get ValueSignal parameter(s), where signal.effectgets invoked only when changes happen on those values.
Additional context
If nothing is provided by Signal API, at least more informative exception can be thrown and should explain how to overcome this issue and what user should do.
How about using Signal.computed(computation)? This at least should solve the "you want to update another value based on the new value" case.
The need for this is typically a symptom of not having a clear model of what is the actual state and what is derived from it. Could you give a concrete example of what you're trying to achieve?
With that being said, a workaround does exist based on the fact that the limitation is implemented by running the callback in a readonly transaction: Signal.runWithoutTransaction(() -> otherSignal.value(foo));
I created a game where users can make at most three mistakes. What I tried initially was to listen to changes on the Game object and try to update another signal -something called GameOver boolean value- when user reached the mistake limit. But I could not do it as it is not allowed to update a Signal value in Signal.effect.
I was not aware of the runWithoutTransaction(). The use case above might cause an infinite loop event with runWithoutTransaction as it will trigger Signal.effect. It would work if you could explicitly set which signal value this code reacts to
That sounds like a perfect case for a computed signal, e.g. something like this:
Signal<Boolean> gameOver = Signal.computed(() -> {
return gameStateSignal.value().mistakes() >= 3;
});
This was already addressed in https://github.com/vaadin/flow/pull/22215