@samitouri / QOS-React / commits / 08725f2132

[compiler]: add `useWindowVirtualizer` to known incompatible libraries (#36912)

## Summary #34493 added `@tanstack/react-virtual`'s `useVirtualizer` to the known incompatible libraries, but not `useWindowVirtualizer`. Both hooks are thin wrappers around the same internal `useVirtualizerBase`, so they return the same referentially-stable virtualizer instance. Its methods (e.g. `getVirtualItems()`, `getTotalSize()`) return internally-mutated values rather than new ones, which is exactly the "interior mutability" pattern that breaks memoization described in the module type provider. Because only `useVirtualizer` was registered, code using `useWindowVirtualizer` is silently compiled with the same unsafe memoization. In our app this froze `getVirtualItems()` to its first-render value (an empty `[]` before measurement), producing permanently-empty virtualized lists — the same class of bug #34493 fixed, just via the window variant. This registers `useWindowVirtualizer` alongside `useVirtualizer` with the identical shape, in **both** implementations: - `compiler/packages/babel-plugin-react-compiler/src/HIR/DefaultModuleTypeProvider.ts` - `compiler/crates/react_compiler_hir/src/default_module_type_provider.rs` (Rust port) See also the community report referenced in #34493: https://github.com/TanStack/virtual/issues/736#issuecomment-3065658277 ## How did you test this change? Each addition mirrors the existing `useVirtualizer` entry exactly (same kind, params, return type, and `knownIncompatible` message shape); both are data-only additions to the module type providers, following the precedent set by #34493 which changed only the TS file. The Rust change keeps the two providers in sync.

gtkatakura committed Jul 6, 2026 at 20:54 UTC 08725f213248f886c7131d406d263fb42b01d501
2 files changed +47 -16
compiler/crates/react_compiler_hir/src/default_module_type_provider.rs
+36 -16
@@ -77,22 +77,42 @@ pub fn default_module_type_provider(module_name: &str) -> Option<TypeConfig> {
77 })),
78
79 "@tanstack/react-virtual" => Some(TypeConfig::Object(ObjectTypeConfig {
80 - properties: Some(IndexMap::from_iter([(
81 - "useVirtualizer".to_string(),
82 - TypeConfig::Hook(HookTypeConfig {
83 - positional_params: Some(Vec::new()),
84 - rest_param: Some(Effect::Read),
85 - return_type: Box::new(TypeConfig::TypeReference(TypeReferenceConfig {
86 - name: BuiltInTypeRef::Any,
87 - })),
88 - return_value_kind: None,
89 - no_alias: None,
90 - aliasing: None,
91 - known_incompatible: Some(
92 - "TanStack Virtual's `useVirtualizer()` API returns functions that cannot be memoized safely".to_string(),
93 - ),
94 - }),
95 - )])),
80 + properties: Some(IndexMap::from_iter([
81 + (
82 + "useVirtualizer".to_string(),
83 + TypeConfig::Hook(HookTypeConfig {
84 + positional_params: Some(Vec::new()),
85 + rest_param: Some(Effect::Read),
86 + return_type: Box::new(TypeConfig::TypeReference(TypeReferenceConfig {
87 + name: BuiltInTypeRef::Any,
88 + })),
89 + return_value_kind: None,
90 + no_alias: None,
91 + aliasing: None,
92 + known_incompatible: Some(
93 + "TanStack Virtual's `useVirtualizer()` API returns functions that cannot be memoized safely".to_string(),
94 + ),
95 + }),
96 + ),
97 + // `useWindowVirtualizer()` wraps the same virtualizer instance as `useVirtualizer()`,
98 + // so its return value is incompatible for the same reason.
99 + (
100 + "useWindowVirtualizer".to_string(),
101 + TypeConfig::Hook(HookTypeConfig {
102 + positional_params: Some(Vec::new()),
103 + rest_param: Some(Effect::Read),
104 + return_type: Box::new(TypeConfig::TypeReference(TypeReferenceConfig {
105 + name: BuiltInTypeRef::Any,
106 + })),
107 + return_value_kind: None,
108 + no_alias: None,
109 + aliasing: None,
110 + known_incompatible: Some(
111 + "TanStack Virtual's `useWindowVirtualizer()` API returns functions that cannot be memoized safely".to_string(),
112 + ),
113 + }),
114 + ),
115 + ])),
116 })),
117
118 _ => None,
compiler/packages/babel-plugin-react-compiler/src/HIR/DefaultModuleTypeProvider.ts
+11
@@ -101,6 +101,17 @@ export function defaultModuleTypeProvider(
101 returnType: {kind: 'type', name: 'Any'},
102 knownIncompatible: `TanStack Virtual's \`useVirtualizer()\` API returns functions that cannot be memoized safely`,
103 },
104 + /*
105 + * `useWindowVirtualizer()` wraps the same virtualizer instance as `useVirtualizer()`, so its return value
106 + * is incompatible for the same reason and we mark the entire hook as incompatible
107 + */
108 + useWindowVirtualizer: {
109 + kind: 'hook',
110 + positionalParams: [],
111 + restParam: Effect.Read,
112 + returnType: {kind: 'type', name: 'Any'},
113 + knownIncompatible: `TanStack Virtual's \`useWindowVirtualizer()\` API returns functions that cannot be memoized safely`,
114 + },
115 },
116 };
117 }