Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
72cd9a32
Unverified
Commit
72cd9a32
authored
Jul 14, 2020
by
Andreas Opferkuch
Committed by
GitHub
Jul 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ThemeContext: Fix useStyles memoization (#26200)
parent
6b6e4778
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
77 additions
and
9 deletions
+77
-9
packages/grafana-ui/src/themes/ThemeContext.test.tsx
+49
-3
packages/grafana-ui/src/themes/ThemeContext.tsx
+28
-6
No files found.
packages/grafana-ui/src/themes/ThemeContext.test.tsx
View file @
72cd9a32
import
React
from
'react'
;
import
React
from
'react'
;
import
{
config
}
from
'@grafana/runtime'
;
import
{
config
}
from
'@grafana/runtime'
;
import
{
renderHook
}
from
'@testing-library/react-hooks'
;
import
{
css
}
from
'emotion'
;
import
{
css
}
from
'emotion'
;
import
{
mount
}
from
'enzyme'
;
import
{
mount
}
from
'enzyme'
;
import
{
useStyles
}
from
'./ThemeContext'
;
import
{
memoizedStyleCreators
,
mockThemeContext
,
useStyles
}
from
'./ThemeContext'
;
describe
(
'useStyles'
,
()
=>
{
describe
(
'useStyles'
,
()
=>
{
it
(
'passes in theme and returns style object'
,
()
=>
{
it
(
'memoizes the passed in function correctly'
,
()
=>
{
const
stylesCreator
=
()
=>
({});
const
{
rerender
,
result
}
=
renderHook
(()
=>
useStyles
(
stylesCreator
));
const
storedReference
=
result
.
current
;
rerender
();
expect
(
storedReference
).
toBe
(
result
.
current
);
});
it
(
'does not memoize if the passed in function changes every time'
,
()
=>
{
const
{
rerender
,
result
}
=
renderHook
(()
=>
useStyles
(()
=>
({})));
const
storedReference
=
result
.
current
;
rerender
();
expect
(
storedReference
).
not
.
toBe
(
result
.
current
);
});
it
(
'updates the memoized function when the theme changes'
,
()
=>
{
const
stylesCreator
=
()
=>
({});
const
{
rerender
,
result
}
=
renderHook
(()
=>
useStyles
(
stylesCreator
));
const
storedReference
=
result
.
current
;
const
restoreThemeContext
=
mockThemeContext
({});
rerender
();
expect
(
storedReference
).
not
.
toBe
(
result
.
current
);
restoreThemeContext
();
});
it
(
'cleans up memoized functions whenever a new one comes along or the component unmounts'
,
()
=>
{
const
styleCreators
:
Function
[]
=
[];
const
{
rerender
,
unmount
}
=
renderHook
(()
=>
{
const
styleCreator
=
()
=>
({});
styleCreators
.
push
(
styleCreator
);
return
useStyles
(
styleCreator
);
});
expect
(
typeof
memoizedStyleCreators
.
get
(
styleCreators
[
0
])).
toBe
(
'function'
);
rerender
();
expect
(
memoizedStyleCreators
.
get
(
styleCreators
[
0
])).
toBeUndefined
();
expect
(
typeof
memoizedStyleCreators
.
get
(
styleCreators
[
1
])).
toBe
(
'function'
);
unmount
();
expect
(
memoizedStyleCreators
.
get
(
styleCreators
[
0
])).
toBeUndefined
();
expect
(
memoizedStyleCreators
.
get
(
styleCreators
[
1
])).
toBeUndefined
();
});
it
(
'passes in theme and returns style object'
,
done
=>
{
const
Dummy
:
React
.
FC
=
function
()
{
const
Dummy
:
React
.
FC
=
function
()
{
const
styles
=
useStyles
(
theme
=>
{
const
styles
=
useStyles
(
theme
=>
{
expect
(
theme
).
toEqual
(
config
.
theme
);
expect
(
theme
).
toEqual
(
config
.
theme
);
return
{
return
{
someStyle
:
css
`
someStyle
:
css
`
color:
${
theme
?
.
palette
.
critical
};
color:
${
theme
.
palette
.
critical
}
;
`
,
`
,
};
};
});
});
expect
(
typeof
styles
.
someStyle
).
toBe
(
'string'
);
expect
(
typeof
styles
.
someStyle
).
toBe
(
'string'
);
done
();
return
<
div
>
dummy
</
div
>;
return
<
div
>
dummy
</
div
>;
};
};
...
...
packages/grafana-ui/src/themes/ThemeContext.tsx
View file @
72cd9a32
import
React
,
{
useContext
}
from
'react
'
;
import
{
GrafanaTheme
,
GrafanaThemeType
}
from
'@grafana/data
'
;
import
hoistNonReactStatics
from
'hoist-non-react-statics'
;
import
hoistNonReactStatics
from
'hoist-non-react-statics'
;
import
React
,
{
useContext
,
useEffect
}
from
'react'
;
import
{
getTheme
}
from
'./getTheme'
;
import
{
Themeable
}
from
'../types/theme'
;
import
{
Themeable
}
from
'../types/theme'
;
import
{
GrafanaTheme
,
GrafanaThemeType
}
from
'@grafana/data
'
;
import
{
getTheme
}
from
'./getTheme
'
;
import
{
stylesFactory
}
from
'./stylesFactory'
;
import
{
stylesFactory
}
from
'./stylesFactory'
;
type
Omit
<
T
,
K
>
=
Pick
<
T
,
Exclude
<
keyof
T
,
K
>>
;
type
Omit
<
T
,
K
>
=
Pick
<
T
,
Exclude
<
keyof
T
,
K
>>
;
...
@@ -14,6 +13,9 @@ type Subtract<T, K> = Omit<T, keyof K>;
...
@@ -14,6 +13,9 @@ type Subtract<T, K> = Omit<T, keyof K>;
*/
*/
let
ThemeContextMock
:
React
.
Context
<
GrafanaTheme
>
|
null
=
null
;
let
ThemeContextMock
:
React
.
Context
<
GrafanaTheme
>
|
null
=
null
;
// Used by useStyles()
export
const
memoizedStyleCreators
=
new
WeakMap
();
// Use Grafana Dark theme by default
// Use Grafana Dark theme by default
export
const
ThemeContext
=
React
.
createContext
(
getTheme
(
GrafanaThemeType
.
Dark
));
export
const
ThemeContext
=
React
.
createContext
(
getTheme
(
GrafanaThemeType
.
Dark
));
ThemeContext
.
displayName
=
'ThemeContext'
;
ThemeContext
.
displayName
=
'ThemeContext'
;
...
@@ -39,9 +41,29 @@ export function useTheme(): GrafanaTheme {
...
@@ -39,9 +41,29 @@ export function useTheme(): GrafanaTheme {
return
useContext
(
ThemeContextMock
||
ThemeContext
);
return
useContext
(
ThemeContextMock
||
ThemeContext
);
}
}
/** Hook for using memoized styles with access to the theme. */
/**
* Hook for using memoized styles with access to the theme.
*
* NOTE: For memoization to work, you need to ensure that the function
* you pass in doesn't change, or only if it needs to. (i.e. declare
* your style creator outside of a function component or use `useCallback()`.)
* */
export function useStyles
<
T
>
(getStyles: (theme: GrafanaTheme) =
>
T)
{
export function useStyles
<
T
>
(getStyles: (theme: GrafanaTheme) =
>
T)
{
return
stylesFactory
(
getStyles
)(
useTheme
());
const
theme
=
useTheme
();
let
memoizedStyleCreator
=
memoizedStyleCreators
.
get
(
getStyles
);
if
(
!
memoizedStyleCreator
)
{
memoizedStyleCreator
=
stylesFactory
(
getStyles
);
memoizedStyleCreators
.
set
(
getStyles
,
memoizedStyleCreator
);
}
useEffect
(()
=>
{
return
()
=>
{
memoizedStyleCreators
.
delete
(
getStyles
);
};
},
[
getStyles
]);
return
memoizedStyleCreator
(
theme
);
}
}
/**
/**
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment