Skip to content

fix(vue3): update reactive globals before calling storyFn#34528

Open
SAY-5 wants to merge 1 commit intostorybookjs:nextfrom
SAY-5:fix/vue-reactive-globals-desync
Open

fix(vue3): update reactive globals before calling storyFn#34528
SAY-5 wants to merge 1 commit intostorybookjs:nextfrom
SAY-5:fix/vue-reactive-globals-desync

Conversation

@SAY-5
Copy link
Copy Markdown

@SAY-5 SAY-5 commented Apr 12, 2026

When a story re-renders without a full remount, globals were being updated after storyFn ran. Decorators would see stale plain objects instead of the reactive proxies, causing the globals state to desync from Vue's reactivity system.

Moved the updateArgs calls for globals and args to happen before storyFn is called, and swapped the context references to use the reactive versions. This way the entire decorator chain sees the same reactive objects that Vue is tracking.

Fixes #34319

Summary by CodeRabbit

  • Bug Fixes
    • Improved reactive state synchronization for Vue3 component rendering to ensure proper update ordering when reusing component instances.

When the story re-renders without a full remount, globals were being
updated after storyFn ran, so decorators saw stale plain objects
instead of the reactive proxies. Moved the updateArgs calls for both
globals and args to happen before storyFn, and replaced the context
references with the reactive versions so the whole chain stays in sync.

Fixes storybookjs#34319
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

The renderToCanvas function in the Vue3 renderer's render.ts changes the timing of reactive state updates when reusing an existing Vue app. Updates to reactive globals and args are now performed before invoking storyFn(), with only reactiveArgs being updated afterward, rather than deferring both updates until after the story function execution.

Changes

Cohort / File(s) Summary
Vue3 Renderer Reactive State Update Sequence
code/renderers/vue3/src/render.ts
Modified the order of reactive state updates in renderToCanvas when reusing an existing Vue app; updateArgs for globals now occurs before storyFn() invocation, and reactiveGlobals is no longer re-updated after story execution (only reactiveArgs is updated post-execution).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/renderers/vue3/src/render.ts (1)

165-167: ⚠️ Potential issue | 🟠 Major

Remove the empty-object early return in updateArgs (bug persists on key removal).

At Line 165, returning early when nextArgs is {} skips the deletion loop, so removed globals/args remain in the reactive object. That keeps stale keys alive and can still desync state.

Proposed fix
 export function updateArgs<
   T extends {
     [name: string]: unknown;
   },
 >(reactiveArgs: T, nextArgs: T) {
-  if (Object.keys(nextArgs).length === 0) {
-    return;
-  }
   const currentArgs = isReactive(reactiveArgs) ? reactiveArgs : reactive(reactiveArgs);
   // delete all args in currentArgs that are not in nextArgs
   Object.keys(currentArgs).forEach((key) => {
     if (!(key in nextArgs)) {
       delete currentArgs[key];
     }
   });
   // update currentArgs with nextArgs
   Object.assign(currentArgs, nextArgs);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/renderers/vue3/src/render.ts` around lines 165 - 167, In updateArgs,
remove the early return that bails when Object.keys(nextArgs).length === 0 so
the subsequent deletion pass runs and removed keys are actually deleted from the
reactive args; ensure the function still compares prevArgs and nextArgs as
before and runs the existing deletion loop (the loop that iterates over
prevArgs/its keys and deletes any key not present in nextArgs) so stale keys are
removed when nextArgs is empty or when keys are removed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@code/renderers/vue3/src/render.ts`:
- Around line 165-167: In updateArgs, remove the early return that bails when
Object.keys(nextArgs).length === 0 so the subsequent deletion pass runs and
removed keys are actually deleted from the reactive args; ensure the function
still compares prevArgs and nextArgs as before and runs the existing deletion
loop (the loop that iterates over prevArgs/its keys and deletes any key not
present in nextArgs) so stale keys are removed when nextArgs is empty or when
keys are removed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 108f4b98-792a-466e-958c-92e92e16e76a

📥 Commits

Reviewing files that changed from the base of the PR and between 5647e6e and 105507a.

📒 Files selected for processing (1)
  • code/renderers/vue3/src/render.ts

@afurm
Copy link
Copy Markdown

afurm commented Apr 13, 2026

Does this also need to apply when forceRemount=true? If a test or story forces a remount, the reactive proxies are recreated but the same globals/args sync issue could still surface in other code paths that don't go through this block.

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.

Vue: Potential desync bug in reactive globals

2 participants