Per-instance config identity defeats the resolved-style cache (2307 entries where 417 suffice)
Maintainer antworten meist innerhalb von 3 Tagen
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 45/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Ruhig
- Tech-Stack
- react-native, typescript
- Bereich
- mobile, performance
Rechercherichtung
Start in src/native/api.tsx, comparing the module-scope mappingToConfig(mapping) in styled() with the per-instance useState call in useCssElement(), then check the identity-based cache key in src/native/react/rules.ts. Read src/components/View.tsx to see how the shipped wrappers pass a constant mapping. Done when repeated identical elements no longer add stylesFamily entries, and the existing native style tests still pass.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Before submitting a new issue
- I tested using the latest version of the library, as the bug might be already fixed.
- I tested using a supported version of react native.
- I checked for possible duplicate issues, with possible answers.
Bug summary
useCssElement derives its style config per component instance, but the resolved-style cache is keyed on that config's object identity. Every shipped components/*.tsx wrapper passes a module-constant mapping, so N mounted elements produce N distinct config identities carrying identical values, and N identical elements each get their own stylesFamily entry, their own sorted rule array and their own observable, where one would do. styled() derives its config once at module scope and shares one entry.
The cache key (src/native/react/rules.ts) hashes the config by identity, through a weakFamily that numbers each new object:
const keys = [state.configs, ...iterableKeys];
The two entry points in src/native/api.tsx derive it differently:
// line 53, styled(): once, at module scope
const configs = mappingToConfig(mapping);
// line 89, useCssElement(): once per instance
const [config] = useState(() => mappingToConfig(mapping));
and every shipped wrapper takes the second path with a module-constant mapping (src/components/View.tsx):
const mapping = {
className: "style",
} satisfies StyledConfiguration<typeof RNView>;
// ...
return useCssElement(RNView, props, mapping);
mappingToConfig is pure in mapping, so it returns the same value and a new identity on every call.
On a screen of 150 identical controls, four styled elements each plus the surrounding chrome, in a release build of React Native 0.86 with Fabric and Hermes on an iPhone 12 Pro Max with iOS 26.6, 3.0.7 held 2307 entries, and 417 with mappingToConfig memoised on the mapping; 417 is the floor of a screen with many distinct class lists. A Mi MIX 3 with 150 identical radio controls held 1785, and 270 with #434. Rendered output is the same either way; the cost is the work and the retained objects. One Instruments CPU run per side over a 160-instance scene put the JS thread at 2914 ms against 2794 ms and 2129 ms against 2082 ms, inside my suite's run-to-run spread, so I can claim no CPU win from my own workload.
#434 fixes it, with these tests.
Library version
main at 2421ea7 (0.0.0-nightly.2421ea7), and 3.0.7
Environment info
System:
OS: Windows 11 10.0.26300
CPU: "(32) x64 AMD Ryzen 9 9950X3D 16-Core Processor "
Memory: 155.26 GB / 255.67 GB
Binaries:
Node:
version: 26.12.1
path: C:\Program Files\nodejs\node.EXE
Yarn:
version: 4.9.2
path: C:\Users\<user>\AppData\Roaming\npm\yarn.CMD
npm:
version: 12.2.1
path: C:\Program Files\nodejs\npm.CMD
Watchman: Not Found
SDKs:
Android SDK:
API Levels:
- "33"
- "35"
- "35"
- "35"
- "36"
- "37"
Build Tools:
- 35.0.0
- 36.0.0
- 36.1.0
- 37.0.0
System Images:
- android-26 | Google APIs Intel x86_64 Atom
- android-28 | Google APIs Intel x86_64 Atom
- android-29 | Google APIs Intel x86_64 Atom
- android-30 | Google APIs Intel x86_64 Atom
- android-31 | Google APIs Intel x86_64 Atom
- android-32 | Google APIs Intel x86_64 Atom
- android-33 | Google APIs Intel x86_64 Atom
- android-34 | Google APIs Intel x86_64 Atom
- android-35 | Google APIs Intel x86_64 Atom
- android-36.1 | Google Play Intel x86_64 Atom
- android-36.1 | Pre-Release 16 KB Page Size Google Play Intel x86_64 Atom
- android-37.0 | Google APIs Intel x86_64 Atom
Android NDK: Not Found
Windows SDK: Not Found
IDEs:
Android Studio: AI-262.9437.185.2621.16467767
Visual Studio: Not Found
Languages:
Java:
version: 17.0.20.1
path: C:\Program Files\Microsoft\jdk-17.0.20.101-hotspot\bin\javac.EXE
Ruby: Not Found
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 19.2.3
wanted: 19.2.3
react-native:
installed: 0.86.3
wanted: 0.86.3
react-native-windows: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: Not found
newArchEnabled: Not found
iOS:
hermesEnabled: Not found
newArchEnabled: Not found
Steps to reproduce
stylesFamily exposes no count on main, so the reproducer branch is main at 2421ea7 with family() given a size(), and one test added, src/__tests__/native/style-cache-per-instance.test.tsx.
- Clone the reproducer branch and install:
git clone --branch repro/433-per-instance-config-identity https://github.com/YevheniiKotyrlo/react-native-css cd react-native-css yarn install - Run
yarn jest src/__tests__/native/style-cache-per-instance.test.tsx.
src/native/reactivity.ts:
diff --git a/src/native/reactivity.ts b/src/native/reactivity.ts
index a32ac64..ad4309c 100644
--- a/src/native/reactivity.ts
+++ b/src/native/reactivity.ts
@@ -173,6 +173,9 @@ export function family<Key, Result = Key, Args extends any = void>(
delete(key: Key) {
return map.delete(key);
},
+ size() {
+ return map.size;
+ },
clear() {
return map.clear();
},
src/__tests__/native/style-cache-per-instance.test.tsx:
// The resolved-style cache holds one entry per distinct class list, so 50 identical elements resolve one style set
import { View as RNView } from "react-native";
import { render } from "@testing-library/react-native";
import { View } from "react-native-css/components/View";
import { registerCSS } from "react-native-css/jest";
import { styled } from "react-native-css/native";
import { stylesFamily } from "../../native/styles";
const StyledView = styled(RNView, { className: { target: "style" } });
test.each([
["View", View],
["styled(View)", StyledView],
])(
"50 identical %s elements hold one resolved-style cache entry",
(_name, Component) => {
registerCSS(`.probe { color: red; width: 10px; }`);
stylesFamily.clear();
render(
<>
{Array.from({ length: 50 }, (_unused, index) => (
<Component key={index} className="probe" />
))}
</>,
);
expect(stylesFamily.size()).toBe(1);
},
);
Expected: 50 identical elements hold one resolved-style cache entry, through the shipped View as through styled(View).
Actual: the shipped View holds 50, one per element, and styled(View) holds one.
The run's output on main at 2421ea7, Windows 11, Node 26.12.1
FAIL src/__tests__/native/style-cache-per-instance.test.tsx (8.787 s)
× 50 identical View elements hold one resolved-style cache entry (26 ms)
√ 50 identical styled(View) elements hold one resolved-style cache entry (6 ms)
● 50 identical View elements hold one resolved-style cache entry
expect(received).toBe(expected) // Object.is equality
Expected: 1
Received: 50
28 | );
29 |
> 30 | expect(stylesFamily.size()).toBe(1);
| ^
31 | },
32 | );
33 |
at toBe (src/__tests__/native/style-cache-per-instance.test.tsx:30:33)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 passed, 2 total
Snapshots: 0 total
Time: 9.86 s
Ran all test suites matching /src\\__tests__\\native\\style-cache-per-instance.test.tsx/i.
Reproducible example repository
https://github.com/YevheniiKotyrlo/react-native-css/tree/repro/433-per-instance-config-identity
- Vorherrschende Sprache
- TypeScript
- Sterne
- 166
- Forks
- 45
- Ø Merge
- 13 T. 14 Std.
- Gemergte PRs (30 T.)
- 1
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus nativewind/react-native-css
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
nativewind/react-native-css#545 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
nativewind/react-native-css#544 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
nativewind/react-native-css#543 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
nativewind/react-native-css#540 ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
nativewind/react-native-css#538 ·
Maintainer antworten meist innerhalb von 3 Tagen
Alle Issues in nativewind/react-native-css
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
lichess-org/lila#22324 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
[Feature Request] [Web] Expose kvCacheQuantizationBits in the onnxruntime-web WebGPU EP optionsOffenep:WebGPU platform:web quantization
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 66/100
microsoft/onnxruntime#33752 ·
Maintainer antworten meist innerhalb von 2 Tagen
-
[Skill Bug]: entra-agent-id: inaccurate blueprint permission inheritance and parent token sub claimOffenbug entra-agent-id
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
microsoft/GitHub-Copilot-for-Azure#3467 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
fix(worker): keep normal prepare waiting states failure-freeEvtl. vergeben @LichKing-2234 hat das heute übernommen. Offenagent:codex priority:P1
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
AgoraIO-Extensions/agent-infra#3745 ·
Maintainer antworten meist innerhalb von 1 Tag
-
The five-line work window can drop its oldest line: the per-line byte share ignores the JSON framingEvtl. vergeben @Eapp1e hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
makecindy/cindy#7973 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag