fix(joint-react): keep the diagram rendered and wired across dev-server hot reloads (HMR) - #3484
Open
samuelgja wants to merge 1 commit into
Open
fix(joint-react): keep the diagram rendered and wired across dev-server hot reloads (HMR)#3484samuelgja wants to merge 1 commit into
samuelgja wants to merge 1 commit into
Conversation
samuelgja
marked this pull request as draft
August 25, 2026 11:21
samuelgja
marked this pull request as ready for review
August 26, 2026 12:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Hot reloading on a dev server (Vite / any Fast Refresh HMR) could leave the diagram blank or unresponsive until a full page reload. Three root causes, all fixed:
GraphProviderkept publishing a destroyed store. A Fast Refresh re-runs all effects of the refreshed component (dependency arrays are ignored), so the provider's mount effect destroys and re-creates itsGraphStore— but readiness was tracked as a boolean, sosetIsReady(true)bailed out and consumers never learned about the new instance: the context kept handing out the destroyed store (cleared graph, dead subscriptions).useImperativeApi()now keeps the instance itself in React state and exposes it; the context value and the provider wiring effect are keyed on the instance identity, so a re-created store is published and re-wired (controlledcellsre-applied included).<Paper>never re-registered against a swapped store. The paper registration effect had[]dependencies, so it stayed bound to the destroyed store forever. It is now keyed on[graphStore, id], so a store re-created while the paper stays mounted gets a fresh registration (and a paperidchange re-registers as well).instanceof PaperViewguards blanked all portals after a module re-evaluation. HMR re-evaluatesmvc/paper.ts, producing a newPaperViewclass identity while live paper instances still come from the previous evaluation; the element/link portal guards rejected them and every portal renderednull. The guards now use a capability check (isPaperView()), which survives re-evaluation. This is the StrictMode-flavored variant of the bug: under StrictMode the strict double-render replay resetsignorePreviousDependencies, so the effect re-run path never fires and only the class-identity hazard remains.Known behavior, unchanged by this fix (uncontrolled mode): a hot reload that re-creates the provider's store re-seeds the graph from
initialCells, so runtime-only edits (cells added at runtime, dragged positions) reset — the same as a remount. This is inherent to the provider owning the store inside an effect. Controlled mode (cells) is re-applied to the new store, and an externalgraphprop is adopted without being cleared, so both preserve state across the reload. This PR targets the blank/broken canvas, not state round-tripping.The regression tests drive the real
react-refreshruntime against the realreact-dom: the suite injects the refresh hook beforereact-domloads, renders, re-evaluates onlygraph-provider.tsxwith all its dependencies shared (the same module-graph shape a Vite HMR update produces), runsperformReactRefresh(), and asserts the diagram stays rendered, does not remount, and remains live (an imperativegraph.addCell()still paints). A second suite pins the cross-evaluationPaperViewguard.react-refreshis added as a dev dependency for these tests.Fixes #3483
Motivation and Context
Editing library source with the storybook/Vite dev server running would "sometimes" — depending on which module the edit invalidated — blank the canvas or freeze subscriptions, forcing a full page reload. This makes HMR reliable in both non-StrictMode apps (effect re-run path) and StrictMode apps (class-identity path).
Screenshots (if appropriate):