Skip to content

Fixing VisualElement mount process#3682

Open
mattgperry wants to merge 1 commit intomainfrom
fix/mount-order
Open

Fixing VisualElement mount process#3682
mattgperry wants to merge 1 commit intomainfrom
fix/mount-order

Conversation

@mattgperry
Copy link
Copy Markdown
Collaborator

No description provided.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 27, 2026

Greptile Summary

This PR fixes the VisualElement mount ordering by extracting the current/visualElementStore assignment into a new register() method, then calling it in use-motion-ref before invoking the external ref callback. This ensures consumers who look up a VisualElement via visualElementStore.get(instance) inside a ref callback will always find it registered. The full mount() (which handles motion-value binding and other side effects) is correctly deferred until after the external ref runs, and register() is idempotent so the second call inside mount() is harmless.

Confidence Score: 4/5

Safe to merge; the only finding is a minor test robustness gap that does not affect production behaviour.

No P0 or P1 issues found. The implementation is correct and idempotent. Score is 4 due to a single P2 (test sentinel value not asserted, leaving a small coverage gap in the new regression test).

packages/framer-motion/src/motion/utils/tests/use-motion-ref.test.tsx — sentinel assertion gap in the new test case.

Important Files Changed

Filename Overview
packages/framer-motion/src/motion/utils/use-motion-ref.ts Calls visualElement.register(instance) before invoking the external ref callback, then calls mount() as before; logic is clean and the double-register is idempotent.
packages/motion-dom/src/render/VisualElement.ts Extracts current/store assignment into a new idempotent register() method and has mount() delegate to it; no behavioural change on the existing mount path.
packages/framer-motion/src/motion/utils/tests/use-motion-ref.test.tsx Adds a regression test for the new ordering guarantee; sentinel value "not-set" is never asserted against, slightly weakening the test.

Sequence Diagram

sequenceDiagram
    participant React
    participant useMotionRef
    participant VisualElement
    participant visualElementStore
    participant ExternalRef

    React->>useMotionRef: ref callback(instance)
    useMotionRef->>VisualElement: register(instance)
    VisualElement->>VisualElement: this.current = instance
    VisualElement->>visualElementStore: set(instance, this)
    useMotionRef->>ExternalRef: externalRef(instance)
    Note over ExternalRef,visualElementStore: Consumer can now resolve VisualElement via visualElementStore.get(instance)
    useMotionRef->>VisualElement: mount(instance)
    VisualElement->>VisualElement: register(instance) [idempotent]
    VisualElement->>VisualElement: bindToMotionValue, update, etc.
Loading

Reviews (1): Last reviewed commit: "Latest" | Re-trigger Greptile

Comment on lines +101 to +102
expect(lookedUpVisualElement).toBeDefined()
expect(lookedUpVisualElement).not.toBeNull()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Sentinel value never asserted — ref callback could silently skip

lookedUpVisualElement is initialised to "not-set", but the test never asserts that it changed from that sentinel. If refCallback were never invoked (e.g., a render regression, wrong instance guard), lookedUpVisualElement would remain "not-set", which is both defined and non-null, so both existing assertions would still pass. Adding expect(lookedUpVisualElement).not.toBe("not-set") would confirm the callback actually fired.

Suggested change
expect(lookedUpVisualElement).toBeDefined()
expect(lookedUpVisualElement).not.toBeNull()
expect(lookedUpVisualElement).not.toBe("not-set")
expect(lookedUpVisualElement).toBeDefined()
expect(lookedUpVisualElement).not.toBeNull()

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