-
-
Notifications
You must be signed in to change notification settings - Fork 32.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Box][styled] Cache makeStyles() per JSON of props for style functions
- Loading branch information
Showing
9 changed files
with
286 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
/* eslint-disable no-console */ | ||
|
||
import Benchmark from 'benchmark'; | ||
import React from 'react'; | ||
import ReactDOMServer from 'react-dom/server'; | ||
import { SheetsRegistry } from 'react-jss'; | ||
import { makeStyles, StylesProvider, styled } from '@material-ui/styles'; | ||
import Box, { styleFunction } from '@material-ui/core/Box'; | ||
|
||
const suite = new Benchmark.Suite('box', { | ||
onError: event => { | ||
console.log(event.target.error); | ||
}, | ||
}); | ||
Benchmark.options.minSamples = 100; | ||
|
||
const cssObject = { | ||
root: { | ||
padding: 2, | ||
margin: 2, | ||
}, | ||
}; | ||
|
||
const useStyles = makeStyles(cssObject); | ||
function HookBox(props) { | ||
const classes = useStyles(); | ||
return <div className={classes.root} {...props} />; | ||
} | ||
|
||
const NoCacheBox = styled('div')(styleFunction, { name: 'MuiBox', _useStylesCache: null }); | ||
|
||
suite | ||
.add('Box', () => { | ||
const sheetsRegistry = new SheetsRegistry(); | ||
ReactDOMServer.renderToString( | ||
<StylesProvider sheetsManager={new Map()} sheetsRegistry={sheetsRegistry}> | ||
{Array.from(new Array(100)).map((_, index) => ( | ||
<Box key={String(index)} padding={2} margin={2}> | ||
Material-UI | ||
</Box> | ||
))} | ||
</StylesProvider>, | ||
); | ||
sheetsRegistry.toString(); | ||
}) | ||
.add('Box with disable props cache', () => { | ||
const sheetsRegistry = new SheetsRegistry(); | ||
ReactDOMServer.renderToString( | ||
<StylesProvider sheetsManager={new Map()} sheetsRegistry={sheetsRegistry}> | ||
{Array.from(new Array(100)).map((_, index) => ( | ||
<NoCacheBox key={String(index)} padding={2} margin={2}> | ||
Material-UI | ||
</NoCacheBox> | ||
))} | ||
</StylesProvider>, | ||
); | ||
sheetsRegistry.toString(); | ||
}) | ||
.add('useStyles', () => { | ||
const sheetsRegistry = new SheetsRegistry(); | ||
ReactDOMServer.renderToString( | ||
<StylesProvider sheetsManager={new Map()} sheetsRegistry={sheetsRegistry}> | ||
{Array.from(new Array(100)).map((_, index) => ( | ||
<HookBox key={String(index)}>Material-UI</HookBox> | ||
))} | ||
</StylesProvider>, | ||
); | ||
sheetsRegistry.toString(); | ||
}) | ||
.add('Inline style', () => { | ||
const sheetsRegistry = new SheetsRegistry(); | ||
ReactDOMServer.renderToString( | ||
<StylesProvider sheetsManager={new Map()} sheetsRegistry={sheetsRegistry}> | ||
{Array.from(new Array(100)).map((_, index) => ( | ||
<div key={String(index)} style={cssObject.root}> | ||
Material-UI | ||
</div> | ||
))} | ||
</StylesProvider>, | ||
); | ||
sheetsRegistry.toString(); | ||
}) | ||
.on('cycle', event => { | ||
console.log(String(event.target)); | ||
}) | ||
.run(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
packages/material-ui-styles/src/makeStyles/useSynchronousEffect.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import React from 'react'; | ||
|
||
export default function useSynchronousEffect(func, values) { | ||
const key = React.useRef([]); | ||
let output; | ||
|
||
// Store "generation" key. Just returns a new object every time | ||
const currentKey = React.useMemo(() => ({}), values); // eslint-disable-line react-hooks/exhaustive-deps | ||
|
||
// "the first render", or "memo dropped the value" | ||
if (key.current !== currentKey) { | ||
key.current = currentKey; | ||
output = func(); | ||
} | ||
|
||
React.useEffect( | ||
() => () => { | ||
if (output) { | ||
output(); | ||
} | ||
}, | ||
[currentKey], // eslint-disable-line react-hooks/exhaustive-deps | ||
); | ||
} |
64 changes: 64 additions & 0 deletions
64
packages/material-ui-styles/src/styled/createCachedUseStyles.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import makeStyles from '../makeStyles'; | ||
import multiKeyStore from '../makeStyles/multiKeyStore'; | ||
import useSynchronousEffect from '../makeStyles/useSynchronousEffect'; | ||
import useTheme from '../useTheme'; | ||
import noopTheme from '../getStylesCreator/noopTheme'; | ||
import warning from 'warning'; | ||
|
||
function pick(input, fields) { | ||
const output = {}; | ||
|
||
Object.keys(input).forEach(prop => { | ||
if (fields.indexOf(prop) >= 0) { | ||
output[prop] = input[prop]; | ||
} | ||
}); | ||
|
||
return output; | ||
} | ||
|
||
export default function createCachedUseStyles({ | ||
styleFunction, | ||
filterProps, | ||
makeStylesOptions, | ||
cacheStore, | ||
}) { | ||
return props => { | ||
const theme = useTheme() || noopTheme; | ||
const propsForStyle = pick(props, filterProps); | ||
|
||
let hasFunc = false; | ||
const cacheKey = JSON.stringify(propsForStyle, (_key, value) => { | ||
if (typeof value !== 'function') return value; | ||
if (!hasFunc) { | ||
warning(false, 'Material-UI: You can not pass a function as style attribute.'); | ||
hasFunc = true; | ||
} | ||
return 'invalid'; | ||
}); | ||
|
||
let cacheEntry = multiKeyStore.get(cacheStore, theme, cacheKey); | ||
if (!cacheEntry) { | ||
cacheEntry = { | ||
useStyles: makeStyles( | ||
{ root: styleFunction({ theme, ...propsForStyle }) }, | ||
{ ...makeStylesOptions, muiDynamic: true }, | ||
), | ||
refs: 0, | ||
}; | ||
if (!hasFunc) multiKeyStore.set(cacheStore, theme, cacheKey, cacheEntry); | ||
} | ||
|
||
useSynchronousEffect(() => { | ||
cacheEntry.refs += 1; | ||
return () => { | ||
cacheEntry.refs -= 1; | ||
if (cacheEntry.refs <= 0) { | ||
multiKeyStore.delete(cacheStore, theme, cacheKey); | ||
} | ||
}; | ||
}, [cacheEntry, theme, cacheKey]); | ||
|
||
return cacheEntry.useStyles(props); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.