revenge-plugins/shared/lib/forceRerender.ts

39 lines
2.8 KiB
TypeScript

// The actual reason YouBar+'s buttons only ever showed up after a full reload: YouBar's own
// button is wrapped in React.memo with the default shallow-prop comparator, and it only ever
// receives one stable boolean prop. Once it mounts, React's own updateMemoComponent bailout means
// it will *never* run its render function again - regardless of how often anything around it
// re-renders - unless something upstream schedules real work that reaches this exact fiber. That
// makes patching `.type` alone invisible until pure luck (an unrelated future re-render) happens
// to touch it, which on a live device can be "eventually" or "never in this session" - matching
// exactly what looked like "needs 2 reloads."
//
// Two earlier versions of this file tried to actively force that render pass:
// - Walking React's DevTools fiber-root registry (__REACT_DEVTOOLS_GLOBAL_HOOK__). Confirmed via
// Key Inspector on a real device that hook doesn't exist on this Discord build at all - that
// whole approach was silently doing nothing.
// - Calling setParams() on the current route via getRootNavigationRef(), to force a genuine
// react-navigation state update. This ran unconditionally, synchronously, the moment the button
// module first initializes - which on a cold boot can be very early, before navigation has
// fully settled. After shipping it, buttons stopped appearing even after a full restart - a
// real regression, not just "still needs a reload." Poking live navigation state as an automatic
// side effect of applying a UI patch is a meaningfully bigger risk than anything else in this
// fix, and it's the prime suspect, so it's been pulled entirely rather than debugged further
// blind. (It also used the cached findByProps instead of this repo's raw* variants, so on a
// build where that lookup wasn't registered yet, it would have permanently poisoned
// getRootNavigationRef for the rest of the session - a second, independent reason to drop it.)
//
// What's left is the one part that's unconditionally safe: if `target` is a memo wrapper,
// permanently override its `compare` so that whenever a future render pass *does* reach this fiber
// for any reason, it can never bail out again. This doesn't make buttons appear instantly on a hot
// toggle - that still needs a real trigger, which is worth another attempt, but only ever tested
// manually first via Key Inspector's Eval tool where its effect can actually be observed before
// it's wired back into anything that runs automatically.
export function forceRerender(target: any): void {
try {
if (target && typeof target === "object" && "compare" in target) {
target.compare = () => false;
}
} catch {
// Best effort - a shape mismatch here just means no permanent bailout override.
}
}