Skip to content

Backport React 19 callback ref cleanup functions - #30

Draft
christopher-buss wants to merge 8 commits into
Roblox:mainfrom
christopher-buss:backport-react-19-ref-cleanup
Draft

Backport React 19 callback ref cleanup functions#30
christopher-buss wants to merge 8 commits into
Roblox:mainfrom
christopher-buss:backport-react-19-ref-cleanup

Conversation

@christopher-buss

@christopher-buss christopher-buss commented Aug 31, 2026

Copy link
Copy Markdown

Summary

Backports React 19 callback-ref cleanup functions to React-Luau. A callback ref may return a cleanup function; React calls that cleanup instead of calling the ref with nil during detachment. The same lifecycle applies to callback refs passed to useImperativeHandle.

Depends on #26 because this branch is stacked on the ref-as-prop backport.

Upstream pins

Source and test ledger

Upstream artifact React-Luau destination Disposition
ReactFiber.new.js ReactFiber.new.lua Direct behavior port: store, initialize, and copy refCleanup
ReactFiber.old.js None Out of scope: this repository ships the new reconciler only
ReactFiberCommitWork.new.js ReactFiberCommitWork.new.lua Direct attach/detach port and original DEV return warning, adapted to the older split commitDetachRef path
ReactFiberCommitWork.old.js None Out of scope: old reconciler
ReactInternalTypes.js ReactInternalTypes.lua Direct Fiber and callback-ref type port
ReactFiberHooks.js ReactFiberHooks.new.lua Direct useImperativeHandle cleanup port translated to Luau
ReactHooks.js / ReactCurrentDispatcher.js / ReactTypes.js ReactHooks.lua / ReactCurrentDispatcher.lua / flowtypes.roblox.lua Public Luau callback-return types
ReactDOM refs-test.js ReactRefCleanup.spec.lua Callback-ref and imperative-handle cases in upstream order, adapted to ReactNoop
ReactFiberCommitWork.new.js DEV warning ReactRefCleanup.spec.lua Public ReactNoop coverage for a legacy detach callback returning a function
d4e78c42 cleanup-versus-null test ReactRefCleanup.spec.lua Direct behavior translation
DOM host renderer assertions ReactNoop assertions Renderer adaptation
DiffTrain compiled artifacts None Out of scope: generated build output

One additional public class-component callback-ref parity test covers the other ref-bearing public fiber shape supported here.

Semantics and deviations

  • A returned cleanup function replaces the legacy callback(nil) detach call for that attachment.
  • If a legacy detach callback itself returns a function, DEV emits the original e982254 warning. React 19.2.8 later removed this warning; this backport retains the original feature-commit contract.
  • Cleanup storage is cleared on both current and alternate fibers as part of every detach, including a throwing one, preventing duplicate cleanup.
  • Luau's xpcall translation clears cleanup storage before dispatching a caught error. This is mechanically equivalent to upstream's finally block and preserves at-most-once cleanup even if error dispatch throws.
  • Object refs retain their existing nil-clearing behavior.
  • The repository's React 17-era unguarded commitDetachRef entry point remains supported and receives equivalent cleanup semantics.
  • Existing profiler behavior is preserved. The older ref-detach path remains untimed rather than importing upstream's layout-effect profiler wrapper; this deviation is recorded at the cleanup branch.
  • Imperative-handle callback results are narrowed from upstream Flow's mixed result to the supported cleanup-function-or-nil shape at the Luau public and dispatcher boundaries.
  • Luau nil represents the upstream null/undefined detach value.
  • Suspense and Activity layout behavior are outside this standalone branch because those later reconciler backports are not present here.

Verification

  • Exact candidate commit: 7cc8646a88ba931cc4623c8e85328cfefa53bfec
  • Exact candidate tree: 653f81154b7ff72dd4a62103f500bedec837b069
  • Changed-file StyLua 0.18.1 check: passed
  • Full Selene 0.28 scan: 0 errors, 0 warnings, 0 parse errors
  • Rojo 7.2.1 build of tests.project.json: passed
  • git diff --check: passed
  • Full StyLua scan: blocked by pre-existing formatting drift in untouched files
  • Hardened single-world identity smoke: 1/1 passed
  • Focused DEV Studio runtime: ReactRefCleanup.spec 1 file, 9/9 tests passed, 0 failed, 0 skipped

Hardened harness record: run-pr30-fd543976-hardened-final.

Reproducibility artifact SHA-256
Candidate archive F8C9AF60A04C1713708C1F97830C578904E1B5B70FCA034269FF66DB6E5CC393
Rojo project EE9B01BA0FA76A94D8EDD2F380AA4C80393A2DA4FFE7C43A23A26B088F96B158
Jest config DE3963AE41E1ACE7CE186070BB2C9012F2DF5072D31357BF61239C06D26D5806
Focused Jest payload E427DD85C11491B07D5395F56C1B9ABD6E17D5C0CFD94157EAA94107243A5916
Identity Jest payload A44BDE708B753EDF73A3F6D15DDB2A589FEC7D8CF46B94EB75289ACB8602818B

Generated with Codex.

Port the React 19 element and reconciler changes while preserving legacy forwardRef and class component compatibility. Include direct Luau ports of the upstream test coverage with each platform deviation stated alongside the code.

Co-authored-by: Codex <noreply@openai.com>
Backport React 19 callback ref cleanups for host, class, and imperative handle refs.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Restore the original detach warning and document the remaining profiler and type adaptations. Refresh callback-ref source links against their pinned upstream revisions.

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Codex <noreply@openai.com>
@christopher-buss
christopher-buss force-pushed the backport-react-19-ref-cleanup branch 2 times, most recently from f47c0c8 to 7cc8646 Compare September 6, 2026 17:20
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