Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(useStyles): add caching when no inline overrides are applied #2309

Merged
merged 39 commits into from
Feb 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
53ae41b
wip
mnajdova Feb 3, 2020
f90e31a
cleanup
mnajdova Feb 3, 2020
4ac7bfd
-prettier fixes
mnajdova Feb 3, 2020
db1e7aa
-disabled tests
mnajdova Feb 3, 2020
77d0748
-test updates
mnajdova Feb 3, 2020
f213a4e
-test fixes
mnajdova Feb 4, 2020
693d237
wip
layershifter Feb 4, 2020
d7be310
Merge branch 'master' into feat/add-style-caching
mnajdova Feb 4, 2020
e4c7fb8
-fixes
mnajdova Feb 4, 2020
6372814
wip
mnajdova Feb 4, 2020
f7fbcd1
prettier fixes
mnajdova Feb 5, 2020
84eb6d4
prettier fixes
mnajdova Feb 5, 2020
7d45f26
-fixes in resolveStylesAndCLasses
mnajdova Feb 5, 2020
6083215
-removed variables cache from Provider's context
mnajdova Feb 5, 2020
3d7732d
-prettier fixes
mnajdova Feb 5, 2020
eb389d5
-added provider flag for enabling caching
mnajdova Feb 7, 2020
53bab76
-prettier fixes
mnajdova Feb 7, 2020
6f3b551
Merge branch 'master' into feat/add-style-caching
mnajdova Feb 7, 2020
214a6ac
-addressing comments
mnajdova Feb 7, 2020
f220251
-addressing comments
mnajdova Feb 7, 2020
0a308bd
prettier fixes
mnajdova Feb 7, 2020
e1f75d3
-updated comments
mnajdova Feb 7, 2020
399e8ac
-refactored key generation
mnajdova Feb 7, 2020
d403c1e
-prettier fixes
mnajdova Feb 7, 2020
697ca82
-added one Provider's prop performance
mnajdova Feb 7, 2020
0b970c4
-moved PrimitiveProps typings
mnajdova Feb 7, 2020
9dae249
prettier fixes
mnajdova Feb 7, 2020
cfb0462
-fixed unit tests
mnajdova Feb 10, 2020
e951ba8
-renamed resolveStylesAndClasses to resolveStyles and merged two func…
mnajdova Feb 10, 2020
ac92aa9
-prettier fixes
mnajdova Feb 10, 2020
40712af
-added component variables cache
mnajdova Feb 10, 2020
f6db0e7
-updated params for resolveStyles
mnajdova Feb 10, 2020
1b91683
-addressed final comments
mnajdova Feb 10, 2020
79f2ce2
prettier fixes
mnajdova Feb 10, 2020
6eea045
Merge branch 'master' into feat/add-style-caching
mnajdova Feb 10, 2020
7b84579
-fixed typings
mnajdova Feb 10, 2020
c03fe01
-updated changelog
mnajdova Feb 10, 2020
3111bbd
-improved tests
mnajdova Feb 10, 2020
d89bf0e
-fixed changelog
mnajdova Feb 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

### BREAKING CHANGES
- Restricted prop set in the `Checkbox`, `Icon`, `Label`, `Slider`, `Status`, `Text` @layershifter ([#2307](https://github.com/microsoft/fluent-ui-react/pull/2307))
- Styles caching when no inline overrides are defined is enabled by default; use the `performance` prop on the `Provider` to opt out of this if needed @mnajdova ([#2309](https://github.com/microsoft/fluent-ui-react/pull/2309))

### Fixes
- Remove dependency on Lodash in TypeScript typings @layershifter ([#2323](https://github.com/microsoft/fluent-ui-react/pull/2323))
Expand All @@ -28,6 +29,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Added sourcemaps to the dist output to simplify debugging @miroslavstastny ([#2329](https://github.com/microsoft/fluent-ui-react/pull/2329))

### Performance
- Add styles caching when there aren't inline overrides defined @mnajdova ([#2309](https://github.com/microsoft/fluent-ui-react/pull/2309))

<!--------------------------------[ v0.44.0 ]------------------------------- -->
## [v0.44.0](https://github.com/microsoft/fluent-ui-react/tree/v0.44.0) (2020-02-05)
[Compare changes](https://github.com/microsoft/fluent-ui-react/compare/v0.43.2..v0.44.0)
Expand Down
14 changes: 11 additions & 3 deletions packages/react-bindings/src/hooks/useStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import { ThemeContext } from 'react-fela'
import {
ComponentDesignProp,
ComponentSlotClasses,
PrimitiveProps,
RendererRenderRule,
StylesContextValue,
} from '../styles/types'
import getStyles from '../styles/getStyles'

type PrimitiveProps = Record<string, boolean | number | string | undefined>
type UseStylesOptions<StyleProps extends PrimitiveProps> = {
className?: string
mapPropsToStyles?: () => StyleProps
Expand Down Expand Up @@ -45,9 +45,12 @@ type InlineStyleProps<StyleProps> = {

const defaultContext: StylesContextValue<{ renderRule: RendererRenderRule }> = {
disableAnimations: false,
performance: {
enableStylesCaching: true,
enableVariablesCaching: true,
},
renderer: { renderRule: () => '' },
theme: emptyTheme,
_internal_resolvedComponentVariables: {},
}

const useStyles = <StyleProps extends PrimitiveProps>(
Expand All @@ -57,6 +60,8 @@ const useStyles = <StyleProps extends PrimitiveProps>(
const context: StylesContextValue<{ renderRule: RendererRenderRule }> =
React.useContext(ThemeContext) || defaultContext

const { enableStylesCaching = true, enableVariablesCaching = true } = context.performance || {}

const {
className = process.env.NODE_ENV === 'production' ? '' : 'no-classname-🙉',
mapPropsToStyles = () => ({} as StyleProps),
Expand All @@ -81,7 +86,10 @@ const useStyles = <StyleProps extends PrimitiveProps>(
rtl,
saveDebug: fluentUIDebug => (debug.current = { fluentUIDebug }),
theme: context.theme,
_internal_resolvedComponentVariables: context._internal_resolvedComponentVariables,
performance: {
enableStylesCaching,
enableVariablesCaching,
},
})

return { classes, styles: resolvedStyles }
Expand Down
122 changes: 22 additions & 100 deletions packages/react-bindings/src/styles/getStyles.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,14 @@
import {
callable,
ComponentSlotStylesInput,
ComponentSlotStylesPrepared,
ComponentSlotStylesResolved,
ComponentStyleFunctionParam,
ComponentVariablesObject,
DebugData,
ICSSInJSStyle,
isDebugEnabled,
mergeComponentStyles,
mergeComponentVariables,
PropsWithVarsAndStyles,
withDebugId,
} from '@fluentui/styles'
import cx from 'classnames'

import * as _ from 'lodash'

import resolveStylesAndClasses from './resolveStylesAndClasses'
import {
ComponentDesignProp,
ComponentSlotClasses,
RendererParam,
RendererRenderRule,
StylesContextValue,
} from '../styles/types'

type GetStylesOptions = StylesContextValue<{
renderRule: RendererRenderRule
}> & {
className?: string
displayName: string
props: PropsWithVarsAndStyles & { design?: ComponentDesignProp }
rtl: boolean
saveDebug: (debug: DebugData | null) => void
}
import { ComponentSlotClasses, ResolveStylesOptions, StylesContextValue } from '../styles/types'
import resolveVariables from './resolveVariables'
import resolveStyles from './resolveStyles'

export type GetStylesResult = {
classes: ComponentSlotClasses
Expand All @@ -42,86 +17,33 @@ export type GetStylesResult = {
theme: StylesContextValue['theme']
}

const getStyles = (options: GetStylesOptions): GetStylesResult => {
const {
className,
disableAnimations,
displayName,
props,
renderer,
rtl,
saveDebug,
theme,
_internal_resolvedComponentVariables: resolvedComponentVariables,
} = options

// Resolve variables for this component, cache the result in provider
if (!resolvedComponentVariables[displayName]) {
resolvedComponentVariables[displayName] =
callable(theme.componentVariables[displayName])(theme.siteVariables) || {} // component variables must not be undefined/null (see mergeComponentVariables contract)
}

// Merge inline variables on top of cached variables
const resolvedVariables = props.variables
? mergeComponentVariables(
resolvedComponentVariables[displayName],
withDebugId(props.variables, 'props.variables'),
)(theme.siteVariables)
: resolvedComponentVariables[displayName]

// Resolve styles using resolved variables, merge results, allow props.styles to override
let mergedStyles: ComponentSlotStylesPrepared = theme.componentStyles[displayName] || {
root: () => ({}),
}

const hasInlineOverrides = !_.isNil(props.design) || !_.isNil(props.styles)

if (hasInlineOverrides) {
mergedStyles = mergeComponentStyles(
mergedStyles,
props.design && withDebugId({ root: props.design }, 'props.design'),
props.styles &&
withDebugId({ root: props.styles } as ComponentSlotStylesInput, 'props.styles'),
)
}

const styleParam: ComponentStyleFunctionParam = {
displayName,
props,
variables: resolvedVariables,
theme,
rtl,
disableAnimations,
}

// Fela plugins rely on `direction` param in `theme` prop instead of RTL
// Our API should be aligned with it
// Heads Up! Keep in sync with Design.tsx render logic
const direction = rtl ? 'rtl' : 'ltr'
const felaParam: RendererParam = {
theme: { direction },
disableAnimations,
displayName, // does not affect styles, only used by useEnhancedRenderer in docs
}

const { resolvedStyles, resolvedStylesDebug, classes } = resolveStylesAndClasses(
mergedStyles,
styleParam,
(style: ICSSInJSStyle) => renderer.renderRule(() => style, felaParam),
const getStyles = (options: ResolveStylesOptions): GetStylesResult => {
//
// To compute styles we are going through three stages:
// - resolve variables (siteVariables => componentVariables + props.variables)
// - resolve styles (with resolvedVariables & props.styles & props.design)
// - compute classes (with resolvedStyles)
// - conditionally add sources for evaluating debug information to component

const resolvedVariables = resolveVariables(
options.displayName,
options.theme,
options.props.variables,
options.performance.enableVariablesCaching,
)

classes.root = cx(className, classes.root, props.className)
const { classes, resolvedStyles, resolvedStylesDebug } = resolveStyles(options, resolvedVariables)

// conditionally add sources for evaluating debug information to component
if (process.env.NODE_ENV !== 'production' && isDebugEnabled) {
saveDebug({
componentName: displayName,
options.saveDebug({
componentName: options.displayName,
componentVariables: _.filter(
resolvedVariables._debug,
variables => !_.isEmpty(variables.resolved),
),
componentStyles: resolvedStylesDebug,
siteVariables: _.filter(theme.siteVariables._debug, siteVars => {
siteVariables: _.filter(options.theme.siteVariables._debug, siteVars => {
if (_.isEmpty(siteVars) || _.isEmpty(siteVars.resolved)) {
return false
}
Expand All @@ -144,7 +66,7 @@ const getStyles = (options: GetStylesOptions): GetStylesResult => {
classes,
variables: resolvedVariables,
styles: resolvedStyles,
theme,
theme: options.theme,
}
}

Expand Down
Loading