Skip to content

Fix reactivity misuses in the motion component body - #79

Open
JonathonRP wants to merge 1 commit into
mainfrom
jonathonrp-improved-meme
Open

Fix reactivity misuses in the motion component body#79
JonathonRP wants to merge 1 commit into
mainfrom
jonathonrp-improved-meme

Conversation

@JonathonRP

Copy link
Copy Markdown
Owner

Two reactivity misuses in createRendererMotionComponent / renderMotionComponent, plus a dead comment block.

1. $state({...}) created inside a $derived.by

configAndProps built its result with $state. The $derived already re-runs when motionConfig / motionProps change, so the inner $state bought nothing — it just allocated a fresh deep Proxy over the entire props graph (style, variants, animate, ...) on every recomputation. That:

  • changes the identity of nested structures VisualElement holds by reference, and
  • deep-proxies user objects that never asked to be reactive.

Nothing mutates the resulting object — VisualElement only ever replaces this.props wholesale in update() — so it is now a plain object literal.

2. context.visualElement mutated onto a derived-owned object

useCreateMotionContext returns a $derived object, and the watcher wrote visualElement onto it. Any variant or tracked prop change rebuilt that object, so context.visualElement read as undefined until the watcher ran again. In that window UseRender loses the element's listeners and measureProps.visualElement goes undefined.

The visual element now lives in init-time $state, and context is a stable object exposing initial / animate from the derived through getters and visualElement through a getter/setter pair. The existing watch.pre assignment is unchanged.

3. Dead code

The commented-out $effect block claimed "useMotionRef is not called on destroy" — it is: UseRender's motionRef attachment teardown calls ref(null), which unmounts the visual element. The note is stale, so the block is removed.

Tests

packages/motion-start/src/motion/__tests__/reactivity.svelte.spec.ts drives the real createRendererMotionComponent with a stub renderer/visual element (probe-motion-component.svelte.ts + ReactivityFixture.svelte) and asserts:

  • nested prop objects (style, variants) reach the visual element by reference rather than as proxy clones;
  • that identity is stable across a recomputation triggered by a variant change;
  • context.visualElement stays defined when read immediately after a variant change, before the commit flushes.

All three fail on main and pass with this change.

Verification

  • vitest run (package): 621 passed. Two pre-existing failures on this machine (package-imports, reorder-production-ssr) are 30s in-test timeouts on disk/build-heavy work and fail identically without this change.
  • svelte-check: 0 errors, 0 warnings.
  • biome check on the changed files: clean apart from the same Svelte-template false positives (noUnusedVariables for template-only bindings) that existing fixtures produce.

Scope note: useLayoutId inside the $derived.by and the broader create-factory / create-proxy / use-render / MotionScope restructure are deliberately untouched.

The merged config/props object was built with `$state` inside a `$derived`. The
derived already recomputes on dependency changes, so the inner `$state` added no
reactivity - it only allocated a fresh deep proxy over the whole props graph on
every recomputation, churning the identity of structures VisualElement holds by
reference and deep-proxying user objects.

The visual element was also assigned onto the object a `$derived` owns, so a
variant change rebuilt that object and left `context.visualElement` undefined
until the watcher reassigned it. It now lives in init-time state the context
reads through a getter.

Also removes a stale commented-out `$effect` block: the visual element is
unmounted by the render attachment's teardown, so the note it carried no longer
holds.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Aug 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 54c63fb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
motion-start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@JonathonRP, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 45 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: eccb9bff-4040-4c9f-8657-3ca68ddf5574

📥 Commits

Reviewing files that changed from the base of the PR and between 0adcef2 and 54c63fb.

📒 Files selected for processing (5)
  • .changeset/smooth-jokes-repeat.md
  • packages/motion-start/src/motion/__tests__/ReactivityFixture.svelte
  • packages/motion-start/src/motion/__tests__/probe-motion-component.svelte.ts
  • packages/motion-start/src/motion/__tests__/reactivity.svelte.spec.ts
  • packages/motion-start/src/motion/index.svelte.ts

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 2, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/motion-start@79

commit: 54c63fb

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