Latest Results
fix(react): MTF variable capture in class components (#2197)
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed captured variables in main-thread functions inside class
components so they update correctly.
* **Refactor**
* Certain class-level worklets are now exposed via computed accessors
instead of initialized fields, which may change initialization timing.
* **Tests**
* Added/updated tests and snapshots covering class methods and class
properties capture scenarios.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Problem
Main-thread worklets inside **class components** could read stale
captured values (e.g. `this.a` in `onTapLepus`), because:
- For `TransformTarget::JS`, worklet ctx objects are later transformed
into functions and invoked with `this` bound to the **ctx object**, not
the component instance.
- When the transform emits a **class field initializer** like
`onTapLepus = { ... a: this.a }`, captured values (`this.a`, or `_c` for
lexical/module vars) are computed once during construction, then remain
frozen on the ctx.
This breaks cases like `onTapLepus` reading the latest `this.a` /
updated module variables.
## What changed
- `swc_plugin_worklet`: For `TransformTarget::JS`, non-static class
members that are worklets now emit a **getter** (`get onTapLepus() {
return { ... } }`) when the worklet captures anything (`_c` / extracted
`this.xxx` / `_jsFn`), so the ctx object is re-created on each access.
- Added helper predicates on the identifier collector to detect whether
`_c` / `this` / `_jsFn` captures exist.
- Coverage: applies to both
- class methods: `class A { onTapLepus(e) { "main thread"; ... } }`
- class properties: `class A { onTapLepus = (e) => { "main thread"; ...
} }`
- Intentionally **JS-only**: we do not emit getters for `LEPUS`/`MIXED`
because the lepus registration path writes `this["onTapLepus"] = ...`,
which can conflict with getter-only accessors.
## Tests
- Updated existing snapshots:
- `should_transform_in_class_js`
- `should_transform_in_class_property_js`
- Added new snapshot tests to ensure “capture non-this values” also
triggers getter:
- `should_transform_in_class_js_capture_values`
- `should_transform_in_class_property_js_capture_values`
## How to review
1. Start at `packages/react/transform/crates/swc_plugin_worklet/lib.rs`:
- `WorkletVisitor::visit_mut_class_member`:
- method branch: when/why it becomes `get onTapLepus()`
- class property branch: same rule for `onTapLepus = (...) => { ... }`
2. Check the new “has captured” predicates in
`packages/react/transform/crates/swc_plugin_worklet/extract_ident.rs`.
3. Review snapshots under
`packages/react/transform/crates/swc_plugin_worklet/tests/__swc_snapshots__/lib.rs/`
and confirm JS output now uses getter in the capture cases.
4. Sanity-check that `LEPUS` output is unchanged for class methods
(still a class field worklet ctx object) to avoid runtime assignment
issues.
## Checklist
- [x] Tests updated (or not required).
- [ ] Documentation updated (or not required).
- [x] Changeset added, and when a BREAKING CHANGE occurs, it needs to be
clearly marked (or not required). Active Branches
#2047-11%
#2199-12%
#2200-6%
© 2026 CodSpeed Technology