From 966d8659f9dcfdc9baa25a234fd4fa298e3961ba Mon Sep 17 00:00:00 2001 From: Andrew Patton Date: Sun, 1 Sep 2024 09:53:19 -0700 Subject: [PATCH] Refactor ); - - const previousStylesRef = useRef(''); - - useEffect(() => { - if (!ownerDocument) return; - - updateStyles({ - ownerDocument, - previousStyles: previousStylesRef.current, - styles, - }); - - previousStylesRef.current = styles; - }, [ownerDocument, styles]); - - const handleRef = useCallback((element: HTMLElement | null) => { - if (!element) return; - setOwnerDocument(element.ownerDocument); - }, []); - - if (ownerDocument) return null; - - // Avoid duplicate style rendering during SSR via style registry - if (!isMountedRef.current) { - if (getRegisteredStyles({ ownerDocument: 'global', styles })) return null; - registerStyles({ ownerDocument: 'global', styles }); - } - - return + + , + ); + + let stylesItemA = styleRegistry.get(mockStylesA); + expect(stylesItemA?.referenceCount).toBe(2); + expect(stylesItemA?.styles).toBe('.test-a { color: cyan; }'); + expect(styleRegistry.size).toBe(1); + + rerender(); + expect(stylesItemA?.referenceCount).toBe(1); + expect(stylesItemA).toBe(styleRegistry.get(mockStylesA)); + expect(styleRegistry.size).toBe(1); + + rerender(); + stylesItemA = styleRegistry.get(mockStylesA); + expect(stylesItemA).toBe(undefined); + let stylesItemB = styleRegistry.get(mockStylesB); + expect(stylesItemB?.referenceCount).toBe(1); + expect(styleRegistry.size).toBe(1); + + rerender( + + + + , + ); + stylesItemA = styleRegistry.get(mockStylesA); + expect(stylesItemA?.referenceCount).toBe(1); + expect(stylesItemA).toBe(styleRegistry.get(mockStylesA)); + stylesItemB = styleRegistry.get(mockStylesB); + expect(stylesItemB?.referenceCount).toBe(1); + expect(styleRegistry.size).toBe(2); + + rerender(
); + expect(styleRegistry.size).toBe(0); + }); + }); +}); diff --git a/packages/styling/src/useStyles.ts b/packages/styling/src/useStyles.ts new file mode 100644 index 00000000..3c0bb9cb --- /dev/null +++ b/packages/styling/src/useStyles.ts @@ -0,0 +1,58 @@ +import { useEffect, useState } from 'react'; + +import { minifyStyles } from './minifyStyles.js'; + +type StyleRegistry = Map; + +const styleRegistry: StyleRegistry = new Map(); + +export const getStyleRegistry = () => styleRegistry; + +export function useStyles(styles: string) { + const [minifiedStyles, setMinifiedStyles] = useState(() => { + if (!styles) return ''; + + let minified = ''; + const existingStylesItem = styleRegistry.get(styles); + if (existingStylesItem) { + existingStylesItem.referenceCount++; + minified = existingStylesItem.styles; + } else { + minified = minifyStyles(styles); + styleRegistry.set(styles, { referenceCount: 1, styles: minified }); + } + + return minified; + }); + + useEffect(() => { + if (!styles) return; + if (!styleRegistry.get(styles)) { + const minified = minifyStyles(styles); + styleRegistry.set(styles, { referenceCount: 1, styles: minified }); + setMinifiedStyles(minified); + } + + return () => { + const stylesItem = styleRegistry.get(styles); + if (stylesItem) { + stylesItem.referenceCount--; + if (!stylesItem.referenceCount) { + // TODO try scheduling this via setTimeout + // and add another referenceCount check + // to deal with instance where existing