SCAL-336321 Skip the pre-render home hop when a new embed shows an equal config - #689
sastaachar wants to merge 1 commit into
Conversation
…ual config React constructs a new LiveboardEmbed on every mount, so the previous === this check never matched and every remount of an unchanged pre-rendered liveboard routed via home and reloaded. Compare viewConfig instead, treating callbacks as equal. SCAL-336321
There was a problem hiding this comment.
Code Review
This pull request introduces a configuration comparison check in LiveboardEmbed to prevent routing via home when a new instance shares an identical configuration with the previous one, and adds corresponding unit tests. Feedback on the implementation highlights a potential performance and recursion issue where lodash/isEqualWith might deeply compare massive, circular DOM elements inside viewConfig. It is recommended to update the customizer to compare Node instances by identity to avoid blocking the UI thread or exceeding the call stack limit.
| // Callbacks are compared as equal: React passes a fresh closure per render. | ||
| private isSameEmbedConfig(previous?: LiveboardEmbed): boolean { | ||
| if (!previous) return false; | ||
| if (previous === this) return true; | ||
| return isEqualWith( | ||
| previous.viewConfig, | ||
| this.viewConfig, | ||
| (a, b) => (typeof a === 'function' && typeof b === 'function' ? true : undefined), | ||
| ); | ||
| } |
There was a problem hiding this comment.
The viewConfig object can contain references to DOM elements (such as preRenderContainer or containerSelector in preRenderConfig which can be HTMLElement / Node instances). When isEqualWith recursively compares viewConfig, it will attempt to deeply compare these DOM elements. Since DOM elements are massive, circular, and complex objects, deeply comparing them is extremely slow, can cause significant performance degradation (UI thread blocking), or even crash the application with a Maximum call stack size exceeded error due to deep recursion.
To prevent this, we should update the customizer to check if the values are instances of Node and compare them by identity (a === b).
// Callbacks are compared as equal: React passes a fresh closure per render.
private isSameEmbedConfig(previous?: LiveboardEmbed): boolean {
if (!previous) return false;
if (previous === this) return true;
return isEqualWith(
previous.viewConfig,
this.viewConfig,
(a, b) => {
if (typeof a === 'function' && typeof b === 'function') {
return true;
}
if (a instanceof Node && b instanceof Node) {
return a === b;
}
return undefined;
},
);
}
Problem
LiveboardEmbed.beforePrerenderVisible()(from #659) routes via home when the pre-render is already on the same liveboard route. That clears state left over from a previous config. It skips the hop only whenprevious === this.React constructs a new
LiveboardEmbedon every mount, so the check never matches there. Every remount of an unchanged<PreRenderedLiveboardEmbed>goes home and reloads the warm liveboard.Change
A hide/show is now detected by config, not identity.
isSameEmbedConfigcomparesprevious.viewConfigwiththis.viewConfigand treats callbacks as equal, since React passes a fresh closure each render. The hop still fires on the same route when the config differs (for example otherruntimeFilters).Version bumped to
1.52.2-beta.1.Testing
SCAL-336321