Skip to content

refactor(core): useAttachment moves to hooks/, and useFingerScroll keeps its watch through it - #9414

Merged
cixzhang merged 2 commits into
meta:mainfrom
vjeux:uikit/f2-use-attachment
Nov 2, 2026
Merged

cixzhang merged 2 commits into
meta:mainfrom
vjeux:uikit/f2-use-attachment

Conversation

@vjeux

@vjeux vjeux commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

On main, after #8360. useDragToSnapPoints (#8658) calls the moved hook once this lands.

Maintainer impact

Three hooks each keep an attach function bound to the node a ref holds across commits: release the replaced node, attach the new one, release at unmount. useGestureRecognizer and usePressTracker share useAttachment for it; useFingerScroll kept its own copy, and useDragToSnapPoints (#8658) needs the same lifecycle outside gestures/. A fix to one copy could miss the other.

Intended invariant

architecture:react-component-runtime INV3: a hook syncs with the DOM node a ref holds after each commit, re-attaches when that node or its key changes, does nothing on a commit that changes neither, and releases at unmount. One helper owns that lifecycle.

Change

  • useFingerScroll keeps its finger-scroll watch through useAttachment, which now takes a node ref, the document's included, instead of its own two Effects.
  • useAttachment moves to hooks/useAttachment.ts, not exported from hooks/index.ts, where hooks outside gestures/ can use it. The gesture-sync depth and useIsUnmountedRef, which only the gesture hooks read, stay in gestures/gestureSync.ts.

Evidence

  • Failure before the change: a new useFingerScroll case pins that the watch lets go of a node it stops following, and of its last node at unmount. It passes on the copy and fails with either release removed from it; after the change it fails with either release removed from useAttachment (7 failing cases each), so the lifecycle has one place to break.
  • Success after the change: every gesture and hook unit case passes.
  • Product/runtime behavior verified unchanged: no shipped component and no interaction spec bundles these hooks; Core's and Lab's entries are unchanged.
  • CI impact: none; no workflow changes. No interaction spec bundles the moved hook, so the WebKit job's scope is unchanged.

Scope

  • No intended public API, product behavior, visual direction, or policy change.
  • Product changes discovered during the work were removed or split.
  • Public text and artifacts contain no internal Meta context.

Testing

Local gates at 4ea3e24, two commits on main (7 files, +171/−86):

  • pnpm test:ui (the UI tests of Core, Lab and Richtext): 714 files, 17,785 tests passed. The tests that load the changed files, by their imports and the pages they bundle, are useFingerScroll.test.tsx and useGestureRecognizer.test.tsx, both in that run; no browser spec loads them.
  • Node, after pnpm build: 843 files passed; only release/cache.test.mjs's two cases fail, as on main (fix(cli): a crashed run's release install is recognized by the Node it ran on, whatever its name #8520). The gesture-free-entries and WebKit-scope tests pass.
  • The interaction specs main's latest changes reach (harness, storybook, MonthScrollerSteps, TabPressPointer, gestures, animatedLayout, animator): Chromium 240 passed; one run on CI's WebKit lane profile, 212 passed.
  • The accessibility suites, whole, on a built Storybook stamped with its commit: Chromium 2,274 passed; WebKit, in CI's container profile, 138 passed and 10 skipped.
  • tsc --project packages/core/tsconfig.json --noEmit, strict ESLint, Prettier, the private-attribute baseline, check:repo, check:knowledge, check:changesets and changeset coverage: clean.

vjeux added 2 commits November 1, 2026 14:28
useFingerScroll kept its own copy of the attachment lifecycle the other
gesture hooks share: follow the node after each commit, release the
replaced node and attach the new one, release at unmount. It now calls
useAttachment, which takes a node ref (an element's, or the document's)
with no change for its callers, so a fix to that lifecycle reaches all
three hooks. A new useFingerScroll case pins that the watch lets go of a
node it stops following and of its last node at unmount; it fails with
either release removed from useAttachment. No behavior changes.
…use it

useAttachment keeps an attach function bound to the node a ref holds
across commits: release the replaced node, attach the new one, release at
unmount. It is a plain React helper, so it moves from gestures/ to
hooks/useAttachment.ts (not exported from hooks/index.ts), where hooks
outside gestures/ can use it instead of copying it. The gesture-sync depth
and useIsUnmountedRef, which only the gesture hooks read, stay in
gestures/gestureSync.ts. useGestureRecognizer, usePressTracker and
useFingerScroll import from the new homes. No behavior changes; no
interaction spec bundles these hooks, so the WebKit job's scope is
unchanged.
@vercel

vercel Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
astryx Ready Ready Preview Nov 2, 2026 12:54am UTC

Request Review

@astracat-bot

astracat-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

AI review status for this pull request.

Review status Updated
🟢 Reviewed (for maintainers only) Nov 2, 2026, 1:01 AM UTC

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Meta Open Source bot. label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

PR Analysis Report

🔎 Preview · 4ea3e24

Storybook · Sandbox · built from this PR's head

Package size: @astryxdesign/core 5.77 MB packed (+678 B); no new dependencies.

API changes: none in this pull request

Committed API report behind this build: core 1 row; run pnpm api:report.

Not run on this PR: add ci:run-visual, ci:run-a11y or ci:run-rtl to run them.


Generated by PR Enrichment workflow | View full report

@cixzhang cixzhang added the ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) label Nov 2, 2026
@cixzhang
cixzhang marked this pull request as ready for review November 2, 2026 00:53

@astracat-bot astracat-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks ready to approve: useFingerScroll now shares useAttachment's attach-and-release lifecycle with the other gesture hooks, with no public API or behavior change, and the new test case fails if either release is removed from the shared helper.

One small fix for the description: its "Intended invariant" quotes INV3 of the React component runtime record with wording that record does not contain; the lifecycle maps to INV5 and INV6, and the single-owner direction to INV16.

[Automated review]

This branch was successfully deployed

1 active deployment
Preview — 4ea3e24c Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:run A maintainer lets this outside contributor's pull request run CI (AST-030 FR17) CLA Signed This label is managed by the Meta Open Source bot.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants