Reconciliation makes more moves than necessary?
- Dominant language
- JavaScript
- Stars
- 1.1k
- Forks
- 34
- PR merge metrics
- No merged PRs in 30d
Description
### Discussed in https://github.com/luwes/sinuous/discussions/168
Originally posted by **mindplay-dk** April 6, 2021
I've been interested in the reconciliation algo for a while, and I'm sort of an idiot when it comes to this stuff - it's really difficult for me to understand the reconciliation code in most of the libraries I've looked at, and frankly I didn't really *trust* that any of them were doing it optimally.
So I set up a use-case driven test bench with a bunch of `console.log` statements, so I can actually try various operations on a list, and see what sort of DOM operations result from those.
Here it is, first for Sinuous - with [diff.js](https://github.com/luwes/sinuous/blob/master/packages/sinuous/map/src/diff.js) from the `map` module:
https://jsfiddle.net/mindplay/gdzrmshq/
Here, I noticed that "Rotate Up" moves 3 elements, while "Rotate Down" moves 2 elements - in either case, one move should be enough, since it's just the first/last element moving to the opposite end of the range.
I understand your code is derived from `udomdiff`? So here's the same test-bench for `udomdiff`:
https://jsfiddle.net/mindplay/o4gbaqw0/
Here, "Rotate Up" takes two moves (one of them a `replaceNode`) which is still one too many - while "Rotate Down" does it accurately, in a single move.
As said, I am an idiot at this stuff, but I decided to try my own naive approach - I simply remove old elements, append new elements, and then take two passes: first, I move only the nodes where moving them isn't going to break the order somewhere else - and then, I take a second pass, moving all remaining nodes that aren't already in the right place.
https://jsfiddle.net/mindplay/924xfvow/
Here, "Rotate Up" and "Rotate Down" both finish in one move.
As said, this approach is totally naive, and probably isn't blazing fast, since I'm using a two passes (and lots of comparisons with next/previous siblings) but I wanted something simple enough that I could understand it myself, being (as stated) an idiot.
Apparently, I'm not a complete idiot, because it works. 😂
I expect this simple algo could be optimized as well, because for most operations (all but "Shuffle") it doesn't actually need to do anything in the second pass - there's only one difference between the first and second pass, and it's this:

So, basically, if that second expression is ever `false` during the first pass, you can return before the second pass, because it would be fully identical to the first pass.
By the way, don't ask me why these loops are backwards. I've no idea if they need to be. I was just trying stuff, and it ended up this way, and it seems to work, so I didn't question it. (Might be good for micro perf anyway.)
I haven't benchmarked or optimized this at all, just throwing it up here for discussion.
Any thoughts? 🙂
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with packages/sinuous/map/src/diff.js and reproduce the rotate-up and rotate-down cases from the linked JSFiddle test benches. Compare the DOM moves produced by the current reconciliation algorithm with the expected single move, then verify that the relevant list operations still produce the correct final DOM order.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- frontend, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100