Skip to content

fix(joint-react): keep the diagram rendered and wired across dev-server hot reloads (HMR) - #3484

Open
samuelgja wants to merge 1 commit into
clientIO:devfrom
samuelgja:fix/dev-server-hot-reload
Open

fix(joint-react): keep the diagram rendered and wired across dev-server hot reloads (HMR)#3484
samuelgja wants to merge 1 commit into
clientIO:devfrom
samuelgja:fix/dev-server-hot-reload

Conversation

@samuelgja

@samuelgja samuelgja commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

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:

  1. GraphProvider kept 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 its GraphStore — but readiness was tracked as a boolean, so setIsReady(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 (controlled cells re-applied included).

  2. <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 paper id change re-registers as well).

  3. instanceof PaperView guards blanked all portals after a module re-evaluation. HMR re-evaluates mvc/paper.ts, producing a new PaperView class identity while live paper instances still come from the previous evaluation; the element/link portal guards rejected them and every portal rendered null. 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 resets ignorePreviousDependencies, 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 external graph prop 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-refresh runtime against the real react-dom: the suite injects the refresh hook before react-dom loads, renders, re-evaluates only graph-provider.tsx with all its dependencies shared (the same module-graph shape a Vite HMR update produces), runs performReactRefresh(), and asserts the diagram stays rendered, does not remount, and remains live (an imperative graph.addCell() still paints). A second suite pins the cross-evaluation PaperView guard. react-refresh is 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):

@samuelgja
samuelgja marked this pull request as draft August 25, 2026 11:21
@samuelgja
samuelgja requested a review from kumilingus August 26, 2026 12:39
@samuelgja
samuelgja marked this pull request as ready for review August 26, 2026 12:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant