Skip to content

Commit 2b20463

Browse files
committed
fix(tasty): cache optimizations
1 parent 450e617 commit 2b20463

File tree

2 files changed

+52
-52
lines changed

2 files changed

+52
-52
lines changed

src/tasty/debug.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -823,7 +823,7 @@ export const tastyDebug = {
823823

824824
console.log('🏷️ Properties & Keyframes:');
825825
console.log(` • Defined @property: ${summary.propertyCount}`);
826-
console.log(` • Defined keyframes: ${summary.keyframeCount}`);
826+
console.log(` • Defined @keyframes: ${summary.keyframeCount}`);
827827

828828
if (metrics) {
829829
console.log(`⚡ Performance Metrics:`);

src/tasty/tasty.tsx

Lines changed: 51 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ import { ResponsiveStyleValue, stringifyStyles } from './utils/styles';
3232
const styleKeyToClass = new Map<string, string>();
3333
let nextClassId = 0;
3434
function allocateClassName(styleKey: string, contextKey?: string): string {
35-
const key = contextKey ? `${styleKey}||${contextKey}` : styleKey;
35+
// Use null character as separator for better performance and no collision risk
36+
const key = contextKey ? `${styleKey}\0${contextKey}` : styleKey;
3637
const existing = styleKeyToClass.get(key);
3738
if (existing) return existing;
3839
const cls = `t${nextClassId++}`;
@@ -88,10 +89,10 @@ type TastyComponentPropsWithDefaults<
8889
> = keyof DefaultProps extends never
8990
? Props
9091
: {
91-
[key in Extract<keyof Props, keyof DefaultProps>]?: Props[key];
92-
} & {
93-
[key in keyof Omit<Props, keyof DefaultProps>]: Props[key];
94-
};
92+
[key in Extract<keyof Props, keyof DefaultProps>]?: Props[key];
93+
} & {
94+
[key in keyof Omit<Props, keyof DefaultProps>]: Props[key];
95+
};
9596

9697
export function tasty<K extends StyleList, V extends VariantMap>(
9798
options: TastyProps<K, V>,
@@ -131,13 +132,12 @@ function tastyGlobal(selector: string, styles?: Styles) {
131132
let contextBreakpoints = useContext(BreakpointsContext);
132133

133134
const breakpointsList = (breakpoints ?? contextBreakpoints) || [980];
134-
const breakpointsHash = breakpointsList.join(',');
135135
const disposeRef = useRef<(() => void) | null>(null);
136136

137137
const styleResults = useMemo(() => {
138138
if (!styles) return [];
139139
return renderStyles(styles, breakpointsList, selector, true);
140-
}, [selector, breakpointsHash]);
140+
}, [selector, styles, breakpointsList]);
141141

142142
// Inject styles at insertion phase; cleanup on change/unmount
143143
useInsertionEffect(() => {
@@ -322,23 +322,21 @@ function tastyElement<K extends StyleList, V extends VariantMap>(
322322
className: userClassName,
323323
...otherProps
324324
} = allProps as Record<string, unknown> as AllBasePropsWithMods<K> &
325-
WithVariant<V> & { className?: string };
325+
WithVariant<V> & { className?: string };
326+
327+
// Optimize propStyles extraction - avoid creating empty objects
328+
let propStyles: Styles | null = null;
329+
const propsToCheck = styleProps
330+
? (styleProps as StyleList).concat(BASE_STYLES)
331+
: BASE_STYLES;
326332

327-
let propStyles: Styles | null = (
328-
(styleProps
329-
? (styleProps as StyleList).concat(BASE_STYLES)
330-
: BASE_STYLES) as StyleList
331-
).reduce((map, prop) => {
333+
for (const prop of propsToCheck) {
332334
const key = prop as unknown as string;
333335
if (Object.prototype.hasOwnProperty.call(otherProps as object, key)) {
334-
(map as any)[key] = (otherProps as any)[key];
336+
if (!propStyles) propStyles = {};
337+
(propStyles as any)[key] = (otherProps as any)[key];
335338
delete (otherProps as any)[key];
336339
}
337-
return map;
338-
}, {} as Styles);
339-
340-
if (Object.keys(propStyles).length === 0) {
341-
propStyles = null;
342340
}
343341

344342
if (
@@ -348,38 +346,39 @@ function tastyElement<K extends StyleList, V extends VariantMap>(
348346
styles = undefined as unknown as Styles;
349347
}
350348

351-
const propStylesCacheKey = stringifyStyles(propStyles);
352-
const stylesCacheKey = useMemo(() => stringifyStyles(styles), [styles]);
353-
354-
const useDefaultStyles = !propStyles && !styles;
355-
356-
const styleCacheKey = useMemo(
357-
() => `${propStylesCacheKey}.${stylesCacheKey}`,
358-
[propStylesCacheKey, stylesCacheKey],
359-
);
360-
361-
let allStyles: Styles | undefined = useMemo(
362-
() =>
363-
useDefaultStyles
364-
? defaultStyles
365-
: mergeStyles(
366-
defaultStyles,
367-
styles as Styles,
368-
propStyles as Styles,
369-
),
370-
[styleCacheKey],
371-
);
372-
373349
let contextBreakpoints = useContext(BreakpointsContext);
374-
375350
breakpoints = (breakpoints as number[] | undefined) ?? contextBreakpoints;
376351

377-
// Allocate a stable sequential class per style-key
378-
const className = useMemo(() => {
379-
const stylesKey = stringifyStyles(allStyles || {});
380-
const bpKey = (breakpoints as number[] | undefined)?.join(',') || '';
381-
return allocateClassName(stylesKey, `bp:${bpKey}`);
382-
}, [allStyles, breakpoints?.join(',')]);
352+
// Memoize breakpoints key once
353+
const breakpointsKey = useMemo(
354+
() => (breakpoints as number[] | undefined)?.join(',') || '',
355+
[breakpoints?.join(',')],
356+
);
357+
358+
// Optimize style computation and className allocation
359+
const { allStyles, className, useDefaultStyles } = useMemo(() => {
360+
const hasStyles =
361+
styles && Object.keys(styles as Record<string, unknown>).length > 0;
362+
const hasPropStyles = propStyles && Object.keys(propStyles).length > 0;
363+
const useDefault = !hasStyles && !hasPropStyles;
364+
365+
const merged = useDefault
366+
? defaultStyles
367+
: mergeStyles(defaultStyles, styles as Styles, propStyles as Styles);
368+
369+
// Single stringifyStyles call
370+
const styleKey = stringifyStyles(merged || {});
371+
const cls = allocateClassName(
372+
styleKey,
373+
breakpointsKey ? `bp:${breakpointsKey}` : undefined,
374+
);
375+
376+
return {
377+
allStyles: merged,
378+
className: cls,
379+
useDefaultStyles: useDefault,
380+
};
381+
}, [styles, propStyles, breakpointsKey]);
383382

384383
// Compute rules synchronously; inject via insertion effect
385384
const directResult: RenderResult = useMemo(() => {
@@ -390,7 +389,7 @@ function tastyElement<K extends StyleList, V extends VariantMap>(
390389
} else {
391390
return { rules: [], className: '' };
392391
}
393-
}, [useDefaultStyles, allStyles, breakpoints?.join(','), className]);
392+
}, [useDefaultStyles, allStyles, breakpointsKey, className]);
394393

395394
const disposeRef = useRef<(() => void) | null>(null);
396395

@@ -456,8 +455,9 @@ function tastyElement<K extends StyleList, V extends VariantMap>(
456455
});
457456
}
458457

459-
_TastyComponent.displayName = `TastyComponent(${(defaultProps as any).qa || originalAs
460-
})`;
458+
_TastyComponent.displayName = `TastyComponent(${
459+
(defaultProps as any).qa || originalAs
460+
})`;
461461

462462
return _TastyComponent;
463463
}

0 commit comments

Comments
 (0)