[fix] StyleSheet registry key check

This commit is contained in:
Nicolas Gallagher
2017-01-02 23:20:28 -08:00
parent 236121e32c
commit 5e4c8e520a
5 changed files with 95 additions and 35 deletions
+1
View File
@@ -82,6 +82,7 @@ Exported modules:
* [`Vibration`](docs/apis/Vibration.md) * [`Vibration`](docs/apis/Vibration.md)
<span id="#why"></span> <span id="#why"></span>
## Why? ## Why?
There are many different teams at Twitter building web applications with React. There are many different teams at Twitter building web applications with React.
+4
View File
@@ -65,6 +65,10 @@ AppRegistry.runApplication('App', {
}) })
``` ```
Setting `process.env.__REACT_NATIVE_DEBUG_ENABLED__` to `true` will expose some
debugging logs. This can help track down when you're rendering without the
performance benefit of cached styles.
## Server-side rendering ## Server-side rendering
Rendering using the `AppRegistry`: Rendering using the `AppRegistry`:
+48 -13
View File
@@ -56,7 +56,7 @@ const registerStyle = (id, flatStyle) => {
} }
}).join(' ').trim(); }).join(' ').trim();
const key = `${prefix}${id}`; const key = `${prefix}-${id}`;
resolvedPropsCache[key] = { className }; resolvedPropsCache[key] = { className };
return id; return id;
@@ -76,6 +76,7 @@ const resolveProps = (reactNativeStyle) => {
if (injectedClassNames[singleClassName]) { if (injectedClassNames[singleClassName]) {
return singleClassName; return singleClassName;
} else { } else {
// 4x slower render
style[prop] = value; style[prop] = value;
} }
} }
@@ -110,11 +111,14 @@ const resolveProps = (reactNativeStyle) => {
* Caching layer over 'resolveProps' * Caching layer over 'resolveProps'
*/ */
const resolvePropsIfNeeded = (key, style) => { const resolvePropsIfNeeded = (key, style) => {
if (!key || !resolvedPropsCache[key]) { if (key) {
// slow: convert style object to props and cache if (!resolvedPropsCache[key]) {
resolvedPropsCache[key] = resolveProps(style); // slow: convert style object to props and cache
resolvedPropsCache[key] = resolveProps(style);
}
return resolvedPropsCache[key];
} }
return resolvedPropsCache[key]; return resolveProps(style);
}; };
/** /**
@@ -123,6 +127,18 @@ const resolvePropsIfNeeded = (key, style) => {
const StyleRegistry = { const StyleRegistry = {
initialize(classNames) { initialize(classNames) {
injectedClassNames = classNames; injectedClassNames = classNames;
if (process.env.__REACT_NATIVE_DEBUG_ENABLED__) {
if (global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer) {
clearInterval(global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer);
}
global.__REACT_NATIVE_DEBUG_ENABLED__styleRegistryTimer = setInterval(() => {
const entryCount = Object.keys(resolvedPropsCache).length;
console.groupCollapsed('[StyleSheet] resolved props cache snapshot:', entryCount, 'entries');
console.log(resolvedPropsCache);
console.groupEnd();
}, 30000);
}
}, },
reset() { reset() {
@@ -155,6 +171,7 @@ const StyleRegistry = {
// flatten the array // flatten the array
// [ 1, [ 2, 3 ], { prop: value }, 4, 5 ] => [ 1, 2, 3, { prop: value }, 4, 5 ]; // [ 1, [ 2, 3 ], { prop: value }, 4, 5 ] => [ 1, 2, 3, { prop: value }, 4, 5 ];
const flatArray = flattenArray(reactNativeStyle); const flatArray = flattenArray(reactNativeStyle);
let isArrayOfNumbers = true; let isArrayOfNumbers = true;
for (let i = 0; i < flatArray.length; i++) { for (let i = 0; i < flatArray.length; i++) {
if (typeof flatArray[i] !== 'number') { if (typeof flatArray[i] !== 'number') {
@@ -163,14 +180,32 @@ const StyleRegistry = {
} }
} }
if (isArrayOfNumbers) { // TODO: determine when/if to cache unregistered styles. This produces 2x
// cache resolved props // faster benchmark results for unregistered styles. However, the cache
const key = `${prefix}${flatArray.join('-')}`; // could be filled with props that are never used again.
return resolvePropsIfNeeded(key, flatArray); //
} else { // let hasValidKey = true;
// resolve // let key = flatArray.reduce((keyParts, element) => {
return resolveProps(flatArray); // if (typeof element === 'number') {
} // keyParts.push(element);
// } else {
// if (element.transform) {
// hasValidKey = false;
// } else {
// const objectAsKey = Object.keys(element).map((prop) => `${prop}:${element[prop]}`).join(';');
// if (objectAsKey !== '') {
// keyParts.push(objectAsKey);
// }
// }
// }
// return keyParts;
// }, [ prefix ]).join('-');
// if (!hasValidKey) { key = null; }
// cache resolved props when all styles are registered
const key = isArrayOfNumbers ? `${prefix}${flatArray.join('-')}` : null;
return resolvePropsIfNeeded(key, flatArray);
} }
}; };
@@ -1,15 +1,25 @@
exports[`components/Text prop "children" 1`] = ` exports[`components/Text prop "children" 1`] = `
<span <span
className="rn-borderTopWidth:0px rn-borderRightWidth:0px rn-borderBottomWidth:0px rn-borderLeftWidth:0px rn-color:inherit rn-display:inline rn-font:inherit rn-marginTop:0px rn-marginRight:0px rn-marginBottom:0px rn-marginLeft:0px rn-paddingTop:0px rn-paddingRight:0px rn-paddingBottom:0px rn-paddingLeft:0px rn-textDecoration:none rn-whiteSpace:pre-wrap rn-wordWrap:break-word" className="
style={ rn-borderTopWidth:0px
Array [ rn-borderRightWidth:0px
3, rn-borderBottomWidth:0px
undefined, rn-borderLeftWidth:0px
false, rn-color:inherit
false, rn-display:inline
undefined, rn-font:inherit
] rn-marginTop:0px
}> rn-marginRight:0px
rn-marginBottom:0px
rn-marginLeft:0px
rn-paddingTop:0px
rn-paddingRight:0px
rn-paddingBottom:0px
rn-paddingLeft:0px
rn-textDecoration:none
rn-whiteSpace:pre-wrap
rn-wordWrap:break-word"
style={Object {}}>
children children
</span> </span>
`; `;
@@ -44,16 +54,26 @@ rn-wordWrap:break-word"
exports[`components/Text prop "selectable" 1`] = ` exports[`components/Text prop "selectable" 1`] = `
<span <span
className="rn-borderTopWidth:0px rn-borderRightWidth:0px rn-borderBottomWidth:0px rn-borderLeftWidth:0px rn-color:inherit rn-display:inline rn-font:inherit rn-marginTop:0px rn-marginRight:0px rn-marginBottom:0px rn-marginLeft:0px rn-paddingTop:0px rn-paddingRight:0px rn-paddingBottom:0px rn-paddingLeft:0px rn-textDecoration:none rn-whiteSpace:pre-wrap rn-wordWrap:break-word" className="
style={ rn-borderTopWidth:0px
Array [ rn-borderRightWidth:0px
3, rn-borderBottomWidth:0px
undefined, rn-borderLeftWidth:0px
false, rn-color:inherit
false, rn-display:inline
undefined, rn-font:inherit
] rn-marginTop:0px
} /> rn-marginRight:0px
rn-marginBottom:0px
rn-marginLeft:0px
rn-paddingTop:0px
rn-paddingRight:0px
rn-paddingBottom:0px
rn-paddingLeft:0px
rn-textDecoration:none
rn-whiteSpace:pre-wrap
rn-wordWrap:break-word"
style={Object {}} />
`; `;
exports[`components/Text prop "selectable" 2`] = ` exports[`components/Text prop "selectable" 2`] = `
+2 -2
View File
@@ -17,11 +17,11 @@ const objects = {};
const prefix = 'r'; const prefix = 'r';
let uniqueID = 1; let uniqueID = 1;
const createKey = (id) => `${prefix}${id}`; const createKey = (id) => `${prefix}-${id}`;
class ReactNativePropRegistry { class ReactNativePropRegistry {
static register(object: Object): number { static register(object: Object): number {
let id = ++uniqueID; let id = uniqueID++;
if (process.env.NODE_ENV !== 'production') { if (process.env.NODE_ENV !== 'production') {
Object.freeze(object); Object.freeze(object);
} }