Open arnaudchenyensu opened 2 months ago
Hi @arnaudchenyensu - sorry not much time to go into more detail right now, but I've prepared a new (more minimal) example with some hopefully helpful comments:
Demo: https://demo.thi.ng/umbrella/rdom-klist/
Source: https://github.com/thi-ng/umbrella/blob/develop/examples/rdom-klist/src/index.ts
https://github.com/thi-ng/umbrella/assets/52302/a30c6fb2-565d-4d27-aadf-e94693822fc4
Please let me know if that answers your Q...
Hi @postspectacular,
Thank you for your example, it's very helpful to understand how to use your libraries!
However, I think that my questions are still unanswered. Even though what I wrote is not good practice, I don't understand why in my question 1, the circle stays grey after a mouse release. In your example v1 you are using the function highlight(sel, i)
but in my example I am directly evaluating the expression fill: clicked === i ? 'grey' : 'white'
. Does it mean that attributes are updated only if a function is given?
And in my question 2, I have duplicate circles even though my keys are similar to the ones in your example v1 (${p}-${i}-${fill}
vs selection|index|item
).
Am I missing something?
Hi @arnaudchenyensu - sorry this is causing so many headaches. I took another closer look at your example and the issue has actually nothing to do with your key
function, but with the way you're updating the atom in your onmousedown
handler.
To remind ourselves once more: each time the atom state is being updated, the atom notifies its watchers, which in this case is the $db = fromAtom(...)
subscription, which then in turn immediately propagates the new state downstream to its own subscribers: in your case this is first the $db.map(...)
subscription used to create items for $klist()
and then inside $klist
another subscription which actually performs the list management & item updates...
Now looking at your onmousedown
handler:
onmousedown: (e: MouseEvent) => {
db.resetIn(['clicked'], i)
db.resetIn(['pts', i], [e.clientX, e.clientY]);
}
These two separate state updates are also triggering two $klist updates, which internally happen partially in async functions, since the mounting & unmounting of rdom components is async. And it's this async behavior of the list update which here causes an out-of-sync issue with the 2 successive updates and break the klist item diffing...
There're (at least) 2 easy solutions to this though (either of them will suffice):
syncRAF()
rstream operator to throttle such multiple state updates and synchronize with the next requestAnimationFrame
and so only cause a single component update (using the latest state)... i.e. change your $klist
input into this:import { syncRAF } from "@thi.ng/rstream";
// ...
$klist(
syncRAF($db).map(({ clicked, pts }): [number, number, Vec][] => pts.map((p, i) => [clicked, i, p])),
// ...
)
defTransacted()
wrapper to perform transacted state updates to the atom itself, i.e. by changing your onmousedown
handler like so:import { defTransacted } from "@thi.ng/atom";
// ...
onmousedown: (e: MouseEvent) => {
const tx = defTransacted(db).begin();
tx.resetIn(["clicked"], i);
tx.resetIn(["pts", i], [e.clientX, e.clientY]);
tx.commit();
}
I've just double checked that each of these two approaches does fix the issue you're having. I will make a note (or accept a PR) to update the docs for $klist
to point out these potential pitfalls and solutions. Thanks for your patience :)
Hi @postspectacular ,
Thank you for your quick reply, it's working a lot better now 😅
Is there any drawbacks from using syncRAF
? Because it seems more "forgiving" when the codebase starts to get bigger.
Hi @postspectacular ,
I'm experimenting a bit with
rdom
and I slightly modified the example from rdom-svg-nodes to change the color of the currently selected circle but I'm having different behaviors (commented in the following code sample) that I don't quite understand:clicked
to be updated automatically because it's coming from$db.map(...)
(just like the circle position is automatically updated fromp
):$klist
doc says that "Like a hash, the key value MUST represent an item's current value such that if the value changes, so does the key." So I thought that I needed to make thefill
color part of the key but it will actually duplicate the circle:Can you help me understand my mistakes?
Thank you in advance 😄