Skip to content

Commit

Permalink
bring back old softMatch behaviour to reduce churn.
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaelWest22 authored and botandrose-machine committed Jan 26, 2025
1 parent 4ffac5d commit 2a09c5a
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 9 deletions.
34 changes: 25 additions & 9 deletions src/idiomorph.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ var Idiomorph = (function () {
/**
* Scans forward from the startPoint to the endPoint looking for a match
* for the node. It looks for an id set match first, then a soft match.
* We abort softmatching if we find two future soft matches, to reduce churn.
* @param {Node} node
* @param {MorphContext} ctx
* @param {Node | null} startPoint
Expand All @@ -381,6 +382,8 @@ var Idiomorph = (function () {
*/
function findBestMatch(ctx, node, startPoint, endPoint) {
let softMatch = null;
let nextSibling = node.nextSibling;
let siblingSoftMatchCount = 0;

let cursor = startPoint;
while (cursor && cursor != endPoint) {
Expand All @@ -391,23 +394,36 @@ var Idiomorph = (function () {
}

// we haven't yet saved a soft match fallback
if (!softMatch) {
if (softMatch === null) {
// the current soft match will hard match something else in the future, leave it
if (!ctx.idMap.has(cursor)) {
// optimization: if node can't id set match, we can just return the soft match immediately
if (!ctx.idMap.has(node)) {
return cursor;
} else {
// save this as the fallback if we get through the loop without finding a hard match
softMatch = cursor;
}
// save this as the fallback if we get through the loop without finding a hard match
softMatch = cursor;
}
}
}
if (
softMatch === null &&
nextSibling &&
isSoftMatch(cursor, nextSibling)
) {
// The next new node has a soft match with this node, so
// increment the count of future soft matches
siblingSoftMatchCount++;
nextSibling = nextSibling.nextSibling;

// If there are two future soft matches, block soft matching for this node to allow
// future siblings to soft match. This is to reduce churn in the DOM when an element
// is prepended.
if (siblingSoftMatchCount >= 2) {
softMatch = undefined;
}
}

cursor = cursor.nextSibling;
}

return softMatch;
return softMatch || null;
}

/**
Expand Down
21 changes: 21 additions & 0 deletions test/ops.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,25 @@ describe("morphing operations", function () {
],
);
});

it("show softMatch aborting on two future soft matches", function () {
// when nodes can't be softMatched because they have different types it will scan ahead
// but it aborts the scan ahead if it finds two nodes ahead in both the new and old content
// that softmatch so it can just insert the mis matched node it is on and get to the matching.
assertOps(
"<section><h1></h1><h2></h2><div></div></section>",
"<section><div>Alert</div><h1></h1><h2></h2><div></div></section>",
[
[
"Morphed",
"<section><h1></h1><h2></h2><div></div></section>",
"<section><div>Alert</div><h1></h1><h2></h2><div></div></section>",
],
["Added", "<div>Alert</div>"],
["Morphed", "<h1></h1>", "<h1></h1>"],
["Morphed", "<h2></h2>", "<h2></h2>"],
["Morphed", "<div></div>", "<div></div>"],
],
);
});
});

0 comments on commit 2a09c5a

Please sign in to comment.