[change] AppRegistry.getApplication returns React elements

This changes the return value of 'getApplication' so that the
application element and stylesheets are all available as React elements.
Also changes StyleSheet's 'renderToString' to 'getStyleSheets'.

Fix #504
This commit is contained in:
Nicolas Gallagher
2017-06-11 14:07:57 -07:00
parent 19381da37f
commit deb0a85440
11 changed files with 118 additions and 89 deletions

View File

@@ -14,8 +14,7 @@ into `runApplication`. These should always be used as a pair.
(web) static **getApplication**(appKey:string, appParameters: object)
Returns the given application element. Use this for server-side rendering.
Return object is of type `{ element: ReactElement; stylesheet: ReactElement }`.
It's recommended that you use `sheetsheet` to render the style sheet in an app
Return object is of type `{ element: ReactElement; stylesheets: [ ReactElement ] }`.
static **registerConfig**(config: Array<AppConfig>)

View File

@@ -16,9 +16,10 @@ Each key of the object passed to `create` must define a style object.
Flattens an array of styles into a single style object.
(web) **renderToString**: function
(web) **getStyleSheets**: function
Returns a string of the stylesheet for use in server-side rendering.
Returns an array of stylesheets (`{ id, textContent }`). Useful for
compile-time or server-side rendering.
## Properties

View File

@@ -187,15 +187,16 @@ const AppContainer = (props) => { /* ... */ }
AppRegistry.registerComponent('App', () => AppContainer)
// prerender the app
const { element, stylesheet } = AppRegistry.getApplication('App', { initialProps });
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
const initialHTML = ReactDOMServer.renderToString(element);
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToString(sheet)).join('\n');
// construct HTML document
const document = `
<!DOCTYPE html>
<html>
<head>
${stylesheet}
${initialStyles}
</head>
<body>
${initialHTML}

View File

@@ -9,63 +9,69 @@ exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
`;
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
.rn-position-bnwqim{position:relative}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-color-homxoj{color:inherit}
.rn-font-1lw9tu2{font:inherit}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-box;display:-moz-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
.rn-flexShrink-1wbh5a2{-webkit-flex-shrink:1;flex-shrink:1}
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
.rn-flexBasis-1ro0kt6{-webkit-flex-basis:0%;flex-basis:0%}
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}
</style>"
Array [
<style
id="react-native-stylesheet-static"
>
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@keyframes rn-ActivityIndicator-animation{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg);}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg);}}
@keyframes rn-ProgressBar-animation{0%{-webkit-transform:translateX(-100%);transform:translateX(-100%);}100%{-webkit-transform:translateX(400%);transform:translateX(400%);}}
.rn-pointerEvents-105ug2t{pointer-events:auto !important;}
.rn-pointerEvents-ah5dr5{pointer-events:auto !important;}
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv &gt; *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 &gt; *{pointer-events:none;}
</style>,
<style
id="react-native-stylesheet"
>
.rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
.rn-position-bnwqim{position:relative}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
.rn-appearance-30o5oe{-moz-appearance:none;-webkit-appearance:none;appearance:none}
.rn-backgroundColor-wib322{background-color:transparent}
.rn-color-homxoj{color:inherit}
.rn-font-1lw9tu2{font:inherit}
.rn-textAlign-1ttztb7{text-align:inherit}
.rn-textDecoration-bauka4{text-decoration:none}
.rn-listStyle-1ebb2ja{list-style:none}
.rn-alignItems-1oszu61{-webkit-align-items:stretch;-webkit-box-align:stretch;align-items:stretch}
.rn-borderTopStyle-1efd50x{border-top-style:solid}
.rn-borderRightStyle-14skgim{border-right-style:solid}
.rn-borderBottomStyle-rull8r{border-bottom-style:solid}
.rn-borderLeftStyle-mm0ijv{border-left-style:solid}
.rn-borderTopWidth-13yce4e{border-top-width:0px}
.rn-borderRightWidth-fnigne{border-right-width:0px}
.rn-borderBottomWidth-ndvcnb{border-bottom-width:0px}
.rn-borderLeftWidth-gxnn5r{border-left-width:0px}
.rn-boxSizing-deolkf{box-sizing:border-box}
.rn-display-6koalj{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}
.rn-display-xoduu5{display:-webkit-box;display:-moz-box;display:-ms-inline-flexbox;display:-webkit-inline-flex;display:inline-flex}
.rn-flexShrink-1qe8dj5{-webkit-flex-shrink:0;flex-shrink:0}
.rn-flexShrink-1wbh5a2{-webkit-flex-shrink:1;flex-shrink:1}
.rn-flexBasis-1mlwlqe{-webkit-flex-basis:auto;flex-basis:auto}
.rn-flexBasis-1ro0kt6{-webkit-flex-basis:0%;flex-basis:0%}
.rn-flexDirection-eqz5dr{-webkit-box-direction:normal;-webkit-box-orient:vertical;-webkit-flex-direction:column;flex-direction:column}
.rn-marginTop-1mnahxq{margin-top:0px}
.rn-marginRight-61z16t{margin-right:0px}
.rn-marginBottom-p1pxzi{margin-bottom:0px}
.rn-marginLeft-11wrixw{margin-left:0px}
.rn-minHeight-ifefl9{min-height:0px}
.rn-minWidth-bcqeeo{min-width:0px}
.rn-paddingTop-wk8lta{padding-top:0px}
.rn-paddingRight-9aemit{padding-right:0px}
.rn-paddingBottom-1mdbw0j{padding-bottom:0px}
.rn-paddingLeft-gy4na3{padding-left:0px}
.rn-zIndex-1lgpqti{z-index:0}
.rn-zIndex-1wyyakw{z-index:-1}
.rn-flexGrow-16y2uox{-webkit-flex-grow:1;flex-grow:1}
</style>,
]
`;

View File

@@ -7,9 +7,9 @@ const RootComponent = () => <div />;
describe('apis/AppRegistry/renderApplication', () => {
test('getApplication', () => {
const { element, stylesheet } = getApplication(RootComponent, {});
const { element, stylesheets } = getApplication(RootComponent, {});
expect(element).toMatchSnapshot();
expect(stylesheet).toMatchSnapshot();
expect(stylesheets).toMatchSnapshot();
});
});

View File

@@ -33,6 +33,8 @@ export function getApplication(RootComponent: ReactClass<Object>, initialProps:
<RootComponent {...initialProps} />
</AppContainer>
);
const stylesheet = StyleSheet.renderToString();
return { element, stylesheet };
const stylesheets = StyleSheet.getStyleSheets().map(sheet =>
<style id={sheet.id}>{sheet.textContent}</style>
);
return { element, stylesheets };
}

View File

@@ -85,6 +85,15 @@ export default class StyleManager {
}
getStyleSheetHtml() {
const styleSheets = this.getStyleSheets();
return styleSheets
.map(sheet => {
return `<style id="${sheet.id}">\n${sheet.textContent}\n</style>`;
})
.join('\n');
}
getStyleSheets() {
const cache = this.cache.byProp;
const mainSheetTextContext = Object.keys(cache)
@@ -99,9 +108,16 @@ export default class StyleManager {
}, [])
.join('\n');
const staticSheet = `<style id="react-native-stylesheet-static">\n${staticCss}\n${pointerEventsCss}\n</style>`;
const mainSheet = `<style id="${STYLE_ELEMENT_ID}">\n${mainSheetTextContext}\n</style>`;
return `${staticSheet}\n${mainSheet}`;
return [
{
id: 'react-native-stylesheet-static',
textContent: `${staticCss}\n${pointerEventsCss}`
},
{
id: STYLE_ELEMENT_ID,
textContent: `${mainSheetTextContext}`
}
];
}
setDeclaration(prop, value) {

View File

@@ -26,8 +26,8 @@ export default class StyleRegistry {
this.styleManager = new StyleManager();
}
getStyleSheetHtml() {
return this.styleManager.getStyleSheetHtml();
getStyleSheets() {
return this.styleManager.getStyleSheets();
}
/**

View File

@@ -1,8 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`apis/StyleSheet renderToString 1`] = `
"<style id=\\"react-native-stylesheet-static\\">
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
exports[`apis/StyleSheet getStyleSheets 1`] = `
Array [
Object {
"id": "react-native-stylesheet-static",
"textContent": "html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
body{margin:0;}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}
input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-webkit-search-results-button,input::-webkit-search-results-decoration{display:none;}
@@ -13,13 +15,15 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
.rn-pointerEvents-633pao{pointer-events:none !important;}
.rn-pointerEvents-12vffkv{pointer-events:none !important;}
.rn-pointerEvents-12vffkv > *{pointer-events:auto;}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}
</style>
<style id=\\"react-native-stylesheet\\">
.rn-bottom-1p0dtai{bottom:0px}
.rn-pointerEvents-ah5dr5 > *{pointer-events:none;}",
},
Object {
"id": "react-native-stylesheet",
"textContent": ".rn-bottom-1p0dtai{bottom:0px}
.rn-left-1d2f490{left:0px}
.rn-position-u8s1d{position:absolute}
.rn-right-zchlnj{right:0px}
.rn-top-ipm5af{top:0px}
</style>"
.rn-top-ipm5af{top:0px}",
},
]
`;

View File

@@ -37,7 +37,7 @@ describe('apis/StyleSheet', () => {
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
});
test('renderToString', () => {
expect(StyleSheet.renderToString()).toMatchSnapshot();
test('getStyleSheets', () => {
expect(StyleSheet.getStyleSheets()).toMatchSnapshot();
});
});

View File

@@ -24,11 +24,11 @@ const StyleSheet = {
});
return result;
},
hairlineWidth: 1,
flatten: flattenStyle,
renderToString() {
return StyleRegistry.getStyleSheetHtml();
}
getStyleSheets() {
return StyleRegistry.getStyleSheets();
},
hairlineWidth: 1
};
export default StyleSheet;