diff --git a/packages/react/src/components/Provider/Provider.tsx b/packages/react/src/components/Provider/Provider.tsx index 99be832c1d..37094fe23a 100644 --- a/packages/react/src/components/Provider/Provider.tsx +++ b/packages/react/src/components/Provider/Provider.tsx @@ -37,8 +37,14 @@ export interface ProviderProps extends ChildrenComponentProps { target?: Document theme?: ThemeInput variables?: ComponentVariablesInput + unstable_performanceBits?: number } +export const PerformanceBitCacheThemeVariables = 0x1 // not implemented yet +export const PerformanceBitMemoizeFelaStyles = 0x2 +export const PerformanceAllBits = + PerformanceBitMemoizeFelaStyles | PerformanceBitCacheThemeVariables + /** * The Provider passes the CSS-in-JS renderer, theme styles and other settings to Stardust components. */ @@ -76,10 +82,12 @@ class Provider extends React.Component> { disableAnimations: PropTypes.bool, children: PropTypes.node.isRequired, target: PropTypes.object, + unstable_performanceBits: PropTypes.number, } static defaultProps = { theme: {}, + unstable_performanceBits: 0, } static Consumer = ProviderConsumer @@ -153,6 +161,7 @@ class Provider extends React.Component> { target, theme, variables, + unstable_performanceBits, ...unhandledProps } = this.props const inputContext: ProviderContextInput = { @@ -167,6 +176,20 @@ class Provider extends React.Component> { // rehydration disabled to avoid leaking styles between renderers // https://github.com/rofrischmann/fela/blob/master/docs/api/fela-dom/rehydrate.md this.outgoingContext = mergeContexts(incomingContext, inputContext) + this.outgoingContext.unstable_performanceBits = unstable_performanceBits + + if (process.env.NODE_ENV !== 'production') { + console.log( + [ + '@stardust-ui/react: following performance optimizations are turned on:', + unstable_performanceBits & PerformanceBitCacheThemeVariables && '- cache theme variables', + unstable_performanceBits & PerformanceBitMemoizeFelaStyles && + '- memoize calls to Fela to avoid plugin execution', + ] + .filter(Boolean) + .join('\n'), + ) + } this.renderStaticStylesOnce(this.outgoingContext.theme) diff --git a/packages/react/src/lib/felaRenderer.tsx b/packages/react/src/lib/felaRenderer.tsx index f267b237e4..71a206f4ce 100644 --- a/packages/react/src/lib/felaRenderer.tsx +++ b/packages/react/src/lib/felaRenderer.tsx @@ -1,9 +1,11 @@ +import { callable } from '@stardust-ui/react-bindings' import { createRenderer as createFelaRenderer } from 'fela' import felaPluginEmbedded from 'fela-plugin-embedded' import felaPluginFallbackValue from 'fela-plugin-fallback-value' import felaPluginPlaceholderPrefixer from 'fela-plugin-placeholder-prefixer' import felaPluginPrefixer from 'fela-plugin-prefixer' import felaPluginRtl from 'fela-plugin-rtl' +import memoize from 'fast-memoize' import { Renderer } from '../themes/types' import felaDisableAnimationsPlugin from './felaDisableAnimationsPlugin' @@ -77,6 +79,14 @@ const rendererConfig = { ], } -export const createRenderer = (): Renderer => createFelaRenderer(rendererConfig) +export const createRenderer = (): Renderer => { + const renderer = createFelaRenderer(rendererConfig) as Renderer + renderer.unstable_memoizedRenderRule = memoize((styles, direction) => { + return renderer.renderRule(callable(styles), { + theme: { direction }, + }) + }) + return renderer +} export const felaRenderer = createRenderer() diff --git a/packages/react/src/lib/renderComponent.tsx b/packages/react/src/lib/renderComponent.tsx index 23a39242af..f675189e1e 100644 --- a/packages/react/src/lib/renderComponent.tsx +++ b/packages/react/src/lib/renderComponent.tsx @@ -34,6 +34,7 @@ import createAnimationStyles from './createAnimationStyles' import { isEnabled as isDebugEnabled } from './debug/debugEnabled' import { DebugData } from './debug/debugData' import withDebugId from './withDebugId' +import { PerformanceBitMemoizeFelaStyles } from '@stardust-ui/react' export interface RenderResultConfig

{ ElementType: React.ElementType

@@ -166,8 +167,13 @@ const renderComponent =

( logProviderMissingWarning() } - const { disableAnimations = false, renderer = null, rtl = false, theme = emptyTheme } = - context || {} + const { + disableAnimations = false, + renderer = null, + rtl = false, + theme = emptyTheme, + unstable_performanceBits = 0, + } = context || {} const ElementType = getElementType(props) as React.ReactType

const stateAndProps = { ...state, ...props } @@ -230,7 +236,14 @@ const renderComponent =

( } if (renderer) { - classes[slotName] = renderer.renderRule(callable(resolvedStyles[slotName]), felaParam) + if (unstable_performanceBits & PerformanceBitMemoizeFelaStyles) { + classes[slotName] = renderer.unstable_memoizedRenderRule( + resolvedStyles[slotName], + direction, + ) + } else { + classes[slotName] = renderer.renderRule(callable(resolvedStyles[slotName]), felaParam) + } } }) diff --git a/packages/react/src/themes/types.ts b/packages/react/src/themes/types.ts index 19b70875ba..bacb40d0f5 100644 --- a/packages/react/src/themes/types.ts +++ b/packages/react/src/themes/types.ts @@ -523,7 +523,9 @@ export type ThemeComponentStylesPrepared = { } & Record -export interface Renderer extends FelaRenderer {} +export interface Renderer extends FelaRenderer { + unstable_memoizedRenderRule: (styles: Record, direction: string) => string +} // ======================================================== // Fonts diff --git a/packages/react/src/types.ts b/packages/react/src/types.ts index 3253ae4549..8833c7a548 100644 --- a/packages/react/src/types.ts +++ b/packages/react/src/types.ts @@ -168,4 +168,5 @@ export interface ProviderContextPrepared { target: Document theme: ThemePrepared originalThemes: (ThemeInput | undefined)[] + unstable_performanceBits?: number }