mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GrafanaUI: Support memoization of useStyles additional arguments (#75000)
* GrafanaUI: Support memoisation of useStyles additional arguments * remove spooky any
This commit is contained in:
parent
f0f1da842b
commit
a54846e75c
@ -77,6 +77,7 @@
|
|||||||
"jquery": "3.7.0",
|
"jquery": "3.7.0",
|
||||||
"lodash": "4.17.21",
|
"lodash": "4.17.21",
|
||||||
"memoize-one": "6.0.0",
|
"memoize-one": "6.0.0",
|
||||||
|
"micro-memoize": "^4.1.2",
|
||||||
"moment": "2.29.4",
|
"moment": "2.29.4",
|
||||||
"monaco-editor": "0.34.0",
|
"monaco-editor": "0.34.0",
|
||||||
"ol": "7.4.0",
|
"ol": "7.4.0",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import React, { HTMLAttributes, useCallback } from 'react';
|
import React, { HTMLAttributes } from 'react';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
@ -19,7 +19,7 @@ export interface BadgeProps extends HTMLAttributes<HTMLDivElement> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip, className, ...otherProps }) => {
|
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip, className, ...otherProps }) => {
|
||||||
const styles = useStyles2(useCallback((theme) => getStyles(theme, color), [color]));
|
const styles = useStyles2(getStyles, color);
|
||||||
const badge = (
|
const badge = (
|
||||||
<div className={cx(styles.wrapper, className)} {...otherProps}>
|
<div className={cx(styles.wrapper, className)} {...otherProps}>
|
||||||
{icon && <Icon name={icon} size="sm" />}
|
{icon && <Icon name={icon} size="sm" />}
|
||||||
|
@ -2,16 +2,36 @@ import { css } from '@emotion/css';
|
|||||||
import { render, renderHook } from '@testing-library/react';
|
import { render, renderHook } from '@testing-library/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
|
|
||||||
import { mockThemeContext, useStyles2 } from './ThemeContext';
|
import { mockThemeContext, useStyles2 } from './ThemeContext';
|
||||||
|
|
||||||
describe('useStyles', () => {
|
describe('useStyles', () => {
|
||||||
it('memoizes the passed in function correctly', () => {
|
it('memoizes the passed in function correctly', () => {
|
||||||
const stylesCreator = () => ({});
|
// implementation has extra arguments to implicitly test the typescript definition of useStyles2
|
||||||
const { rerender, result } = renderHook(() => useStyles2(stylesCreator));
|
const getStyles = jest.fn((theme: GrafanaTheme2, isOdd: boolean) => ({ row: 'row-class-name' }));
|
||||||
const storedReference = result.current;
|
|
||||||
|
|
||||||
rerender();
|
function Row({ isOdd }: { isOdd: boolean }) {
|
||||||
expect(storedReference).toBe(result.current);
|
const styles = useStyles2(getStyles, isOdd);
|
||||||
|
return <div className={styles.row} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TestUseStyles() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Row isOdd={true} />
|
||||||
|
<Row isOdd={false} />
|
||||||
|
<Row isOdd={true} />
|
||||||
|
<Row isOdd={false} />
|
||||||
|
<Row isOdd={true} />
|
||||||
|
<Row isOdd={false} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(<TestUseStyles />);
|
||||||
|
|
||||||
|
expect(getStyles).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not memoize if the passed in function changes every time', () => {
|
it('does not memoize if the passed in function changes every time', () => {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import hoistNonReactStatics from 'hoist-non-react-statics';
|
import hoistNonReactStatics from 'hoist-non-react-statics';
|
||||||
|
import memoize from 'micro-memoize';
|
||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
|
||||||
import { createTheme, GrafanaTheme, GrafanaTheme2 } from '@grafana/data';
|
import { createTheme, GrafanaTheme, GrafanaTheme2 } from '@grafana/data';
|
||||||
@ -89,6 +90,7 @@ export function useStyles<T>(getStyles: (theme: GrafanaTheme) => T) {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
let memoizedStyleCreator: typeof getStyles = memoizedStyleCreators.get(getStyles);
|
let memoizedStyleCreator: typeof getStyles = memoizedStyleCreators.get(getStyles);
|
||||||
|
|
||||||
if (!memoizedStyleCreator) {
|
if (!memoizedStyleCreator) {
|
||||||
memoizedStyleCreator = stylesFactory(getStyles);
|
memoizedStyleCreator = stylesFactory(getStyles);
|
||||||
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
|
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
|
||||||
@ -98,23 +100,34 @@ export function useStyles<T>(getStyles: (theme: GrafanaTheme) => T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook for using memoized styles with access to the theme.
|
* Hook for using memoized styles with access to the theme. Pass additional
|
||||||
|
* arguments to the getStyles function as additional arguments to this hook.
|
||||||
*
|
*
|
||||||
* NOTE: For memoization to work, you need to ensure that the function
|
* Prefer using primitive values (boolean, number, string, etc) for
|
||||||
* you pass in doesn't change, or only if it needs to. (i.e. declare
|
* additional arguments for better performance
|
||||||
* your style creator outside of a function component or use `useCallback()`.)
|
*
|
||||||
|
* const getStyles = (theme, isDisabled, isOdd) => {css(...)}
|
||||||
|
* [...]
|
||||||
|
* const styles = useStyles2(getStyles, true, Boolean(index % 2))
|
||||||
|
*
|
||||||
|
* NOTE: For memoization to work, ensure that all arguments don't change
|
||||||
|
* across renders (or only change if they need to)
|
||||||
* */
|
* */
|
||||||
/** @public */
|
/** @public */
|
||||||
export function useStyles2<T>(getStyles: (theme: GrafanaTheme2) => T) {
|
export function useStyles2<T extends unknown[], CSSReturnValue>(
|
||||||
|
getStyles: (theme: GrafanaTheme2, ...args: T) => CSSReturnValue,
|
||||||
|
...additionalArguments: T
|
||||||
|
): CSSReturnValue {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
|
|
||||||
let memoizedStyleCreator: typeof getStyles = memoizedStyleCreators.get(getStyles);
|
let memoizedStyleCreator: typeof getStyles = memoizedStyleCreators.get(getStyles);
|
||||||
|
|
||||||
if (!memoizedStyleCreator) {
|
if (!memoizedStyleCreator) {
|
||||||
memoizedStyleCreator = stylesFactory(getStyles);
|
memoizedStyleCreator = memoize(getStyles, { maxSize: 10 }); // each getStyles function will memoize 10 different sets of props
|
||||||
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
|
memoizedStyleCreators.set(getStyles, memoizedStyleCreator);
|
||||||
}
|
}
|
||||||
|
|
||||||
return memoizedStyleCreator(theme);
|
return memoizedStyleCreator(theme, ...additionalArguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -4151,6 +4151,7 @@ __metadata:
|
|||||||
jquery: 3.7.0
|
jquery: 3.7.0
|
||||||
lodash: 4.17.21
|
lodash: 4.17.21
|
||||||
memoize-one: 6.0.0
|
memoize-one: 6.0.0
|
||||||
|
micro-memoize: ^4.1.2
|
||||||
mock-raf: 1.0.1
|
mock-raf: 1.0.1
|
||||||
moment: 2.29.4
|
moment: 2.29.4
|
||||||
monaco-editor: 0.34.0
|
monaco-editor: 0.34.0
|
||||||
@ -24412,6 +24413,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"micro-memoize@npm:^4.1.2":
|
||||||
|
version: 4.1.2
|
||||||
|
resolution: "micro-memoize@npm:4.1.2"
|
||||||
|
checksum: 4b02750622d44b5ab31573c629b5d91927dd0c2727743ff75e790c223ab6cd02c48cc3bddea69da0dffb688091a0a71a17944947dd165f8ba9e03728bc30a76d
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4":
|
"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4":
|
||||||
version: 4.0.4
|
version: 4.0.4
|
||||||
resolution: "micromatch@npm:4.0.4"
|
resolution: "micromatch@npm:4.0.4"
|
||||||
|
Loading…
Reference in New Issue
Block a user