Laminar v17.2.1
Airstream bug fixes!
Laminar is a Scala.js library for building web application interfaces and managing UI state. Learn more in a few short examples or one long video.
Releases
Airstream Bug Fixes
Laminar 17.2.1 release consists entirely of two Airstream bug fixes. The fix slightly changes Airstream behaviour, although you are unlikely to be relying on the current incorrect behaviour.
Fixed: flattenSwitch / flatMapSwitch unwanted restart issue
When flattenSwitch
(or flatMapSwitch
) operator switches from signal1
to signal2
, it first disconnects from signal1
before connecting to signal2
. Usually this is the right thing to do.
Our code already had a special case – we don't do this if signal1 == signal2
– in that case we don't need to restart the same signal for no reason, so we just keep it active, without power-cycling it.
However, there is a more advanced variation of this special case that we failed to consider previously – if both signal1
and signal2
have a common ancestor signal0
, and signal0
has no other observers, switching from signal1
to signal2
will cause signal0
to briefly stop and then re-start.
This is undesirable because some signals may have custom onStart logic in them – for example, EventStream.fromValue(1).toSignal(0)
will emit 1
every time it's started (by design), so we don't want to re-start such a signal when the user does not explicit mean to, as it would produce an unexpected event – a glitch.
The 17.2.1 release fixes this with a make-before-break mechanism – now, when flattenSwitch
switches from signal1
to signal2
, it first connects to the new signal2
source before disconnecting from the old signal1
source. This ensures that any common ancestors of the old and new signals remain active and are not re-started unnecessarily.
All of this applies to streams just as well as signals.
Migration: You may have flattenSwitch
/ flatMapSwitch
signals (or streams) that currently emit extra events due to this bug, and they will stop emitting those events. I believe those glitchy events are entirely undesirable, so if everything is working fine for you right now, chances are it will continue working fine, but if you have experienced weird / unexplained events when flattening observables, this may just fix it.
Fixed: split signal's child looking at inactive split signal
SplitChildSignal
(the signal that the split
operator's callback gives you for each item) reads the internal state of SplitSignal
(the result of the .split
operator itself) to do its thing, but does not formally depend on it as an observable.
This means that if SplitChildSignal
has observers, and SplitSignal
does not, then SplitSignal
is not running and so it is not updating its internal state. IN this scenario, SplitChildSignal
can potentially read stale/incorrect state from SplitSignal
, resulting in SplitSignal
emitting a stale/incorrect version of its item, or failing to emit when it's due.
We fixed this by properly emulating SplitChildSignal
's dependency on SplitSignal
. Our SplitChildSignal
is perhaps the weirdest signal in Airstream on a technical/implementation level, so it needed special care for this.
Migration: In practice, you are unlikely to encounter this bug when using typical Laminar patterns. And if you did, you would likely notice SplitChildSignal
emitting incorrect values.
Artūras Šlajus has provided a great example of these two bugs working together to sabotage two nested split
-s followed by a map
and a flattenSwitch
.
Thank you
Laminar development is kindly supported by my sponsors, and I am very grateful to be able to work on all this.
DIAMOND sponsor:
GOLD sponsors:
