mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-23 23:06:24 +00:00
[change] AppRegistry and StyleSheet APIs to fix SSR
SSR was not working correctly. Styles would accumulate in the style
cache and the styles returned for each 'getApplication' call would not
represent the styles needed to render a given tree, but rather all the
styles needed to render every tree that has been rendered by that server
instance.
This is now fixed by reinitializing the style resolver after a call to
'getStyleSheet' on the server. The return type of
'AppRegistry.getApplication' is changed – { element, getStyleElement }.
The 'getStyleElement' function must be called after the component tree
has been rendered. Note that if 'getStyleElement' is not called for a
response, then its styles may leak into the next response's styles (but
will not affect the UX).
This patch also removes the 'StyleSheet.getStyleSheets' (web-only) API
and requires SSR to be done using 'AppRegistry.getApplication'.
Fix #778
This commit is contained in:
+58
-2
@@ -1,6 +1,15 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
|
||||
exports[`Additional CSS for styled app 1`] = `
|
||||
"
|
||||
.rn-backgroundColor-1e4kli0{background-color:purple}
|
||||
.rn-borderTopWidth-10pzpfo{border-top-width:1234px}
|
||||
.rn-borderRightWidth-1y24uml{border-right-width:1234px}
|
||||
.rn-borderBottomWidth-98wxn4{border-bottom-width:1234px}
|
||||
.rn-borderLeftWidth-150mub4{border-left-width:1234px}"
|
||||
`;
|
||||
|
||||
exports[`AppRegistry/renderApplication getApplication returns "element" and "getStyleElement" 1`] = `
|
||||
<AppContainer
|
||||
rootTag={Object {}}
|
||||
>
|
||||
@@ -8,7 +17,7 @@ exports[`apis/AppRegistry/renderApplication getApplication 1`] = `
|
||||
</AppContainer>
|
||||
`;
|
||||
|
||||
exports[`apis/AppRegistry/renderApplication getApplication 2`] = `
|
||||
exports[`AppRegistry/renderApplication getApplication returns "element" and "getStyleElement" 2`] = `
|
||||
"<style id=\\"react-native-stylesheet\\">@media all{
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:rgba(0,0,0,0);}
|
||||
body{margin:0;}
|
||||
@@ -18,3 +27,50 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
|
||||
@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%);}}</style>"
|
||||
`;
|
||||
|
||||
exports[`CSS for an unstyled app 1`] = `
|
||||
"@media all{
|
||||
html{-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-alignItems-1oszu61{-ms-flex-align:stretch;-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-flexShrink-1pxmb3b{-ms-flex-negative:0 !important;-webkit-flex-shrink:0 !important;flex-shrink:0 !important}
|
||||
.rn-flexBasis-7vfszb{-ms-flex-preferred-size:auto !important;-webkit-flex-basis:auto !important;flex-basis:auto !important}
|
||||
.rn-flexDirection-eqz5dr{-ms-flex-direction:column;-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-position-bnwqim{position:relative}
|
||||
.rn-zIndex-1lgpqti{z-index:0}
|
||||
.rn-flex-13awgt0{-ms-flex:1;-webkit-flex:1;flex:1}
|
||||
.rn-flexGrow-1m1wadx{-ms-flex-positive:1 !important;-webkit-flex-grow:1 !important;flex-grow:1 !important}
|
||||
.rn-flexShrink-1awmn5t{-ms-flex-negative:1 !important;-webkit-flex-shrink:1 !important;flex-shrink:1 !important}
|
||||
.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}
|
||||
.rn-pointerEvents-12vffkv > *{pointer-events:auto}
|
||||
.rn-pointerEvents-12vffkv{pointer-events:none !important}"
|
||||
`;
|
||||
|
||||
+49
-7
@@ -1,19 +1,61 @@
|
||||
/* eslint-env jasmine, jest */
|
||||
|
||||
import ExecutionEnvironment from 'fbjs/lib/ExecutionEnvironment';
|
||||
import { getApplication } from '../renderApplication';
|
||||
import React from 'react';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { render } from 'enzyme';
|
||||
import StyleSheet from '../../StyleSheet';
|
||||
import View from '../../View';
|
||||
|
||||
const RootComponent = () => <div />;
|
||||
|
||||
describe('apis/AppRegistry/renderApplication', () => {
|
||||
test('getApplication', () => {
|
||||
const { element, stylesheets } = getApplication(RootComponent, {});
|
||||
describe('AppRegistry/renderApplication', () => {
|
||||
describe('getApplication', () => {
|
||||
const canUseDOM = ExecutionEnvironment.canUseDOM;
|
||||
|
||||
expect(element).toMatchSnapshot();
|
||||
stylesheets.forEach(sheet => {
|
||||
const result = ReactDOMServer.renderToStaticMarkup(sheet);
|
||||
expect(result).toMatchSnapshot();
|
||||
beforeEach(() => {
|
||||
ExecutionEnvironment.canUseDOM = false;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
ExecutionEnvironment.canUseDOM = canUseDOM;
|
||||
});
|
||||
|
||||
test('returns "element" and "getStyleElement"', () => {
|
||||
const { element, getStyleElement } = getApplication(RootComponent, {});
|
||||
expect(element).toMatchSnapshot();
|
||||
expect(ReactDOMServer.renderToStaticMarkup(getStyleElement())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('"getStyleElement" produces styles that are a function of rendering "element"', () => {
|
||||
const getTextContent = getStyleElement =>
|
||||
getStyleElement().props.dangerouslySetInnerHTML.__html;
|
||||
|
||||
// First "RootComponent" render
|
||||
let app = getApplication(RootComponent, {});
|
||||
render(app.element);
|
||||
const first = getTextContent(app.getStyleElement);
|
||||
|
||||
// Next render is a different tree; the style sheet should be different
|
||||
const styles = StyleSheet.create({ root: { borderWidth: 1234, backgroundColor: 'purple' } });
|
||||
app = getApplication(() => <View style={styles.root} />, {});
|
||||
render(app.element);
|
||||
const second = getTextContent(app.getStyleElement);
|
||||
|
||||
const diff = second.split(first)[1];
|
||||
|
||||
expect(first).toMatchSnapshot('CSS for an unstyled app');
|
||||
expect(diff).toMatchSnapshot('Additional CSS for styled app');
|
||||
expect(first).not.toEqual(second);
|
||||
|
||||
// Final render is once again "RootComponent"; the style sheet should not
|
||||
// be polluted by earlier rendering of a different tree
|
||||
app = getApplication(RootComponent, {});
|
||||
render(app.element);
|
||||
const third = getTextContent(app.getStyleElement);
|
||||
|
||||
expect(first).toEqual(third);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,7 +13,7 @@ import AppContainer from './AppContainer';
|
||||
import invariant from 'fbjs/lib/invariant';
|
||||
import hydrate from '../../modules/hydrate';
|
||||
import render from '../render';
|
||||
import StyleSheet from '../StyleSheet';
|
||||
import styleResolver from '../StyleSheet/styleResolver';
|
||||
import React, { type ComponentType } from 'react';
|
||||
|
||||
const renderFn = process.env.NODE_ENV !== 'production' ? render : hydrate;
|
||||
@@ -39,9 +39,10 @@ export function getApplication(RootComponent: ComponentType<Object>, initialProp
|
||||
<RootComponent {...initialProps} />
|
||||
</AppContainer>
|
||||
);
|
||||
const stylesheets = StyleSheet.getStyleSheets().map(sheet => (
|
||||
// ensure that CSS text is not escaped
|
||||
<style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} key={sheet.id} />
|
||||
));
|
||||
return { element, stylesheets };
|
||||
// Don't escape CSS text
|
||||
const getStyleElement = () => {
|
||||
const sheet = styleResolver.getStyleSheet();
|
||||
return <style dangerouslySetInnerHTML={{ __html: sheet.textContent }} id={sheet.id} />;
|
||||
};
|
||||
return { element, getStyleElement };
|
||||
}
|
||||
|
||||
+26
-15
@@ -11,6 +11,7 @@
|
||||
* @noflow
|
||||
*/
|
||||
|
||||
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
import createReactDOMStyle from './createReactDOMStyle';
|
||||
import flattenArray from '../../modules/flattenArray';
|
||||
import flattenStyle from './flattenStyle';
|
||||
@@ -22,12 +23,22 @@ import StyleSheetManager from './StyleSheetManager';
|
||||
const emptyObject = {};
|
||||
|
||||
export default class ReactNativeStyleResolver {
|
||||
cache = { ltr: {}, rtl: {} };
|
||||
_init() {
|
||||
this.cache = { ltr: {}, rtl: {} };
|
||||
this.styleSheetManager = new StyleSheetManager();
|
||||
}
|
||||
|
||||
styleSheetManager = new StyleSheetManager();
|
||||
constructor() {
|
||||
this._init();
|
||||
}
|
||||
|
||||
getStyleSheets() {
|
||||
return this.styleSheetManager.getStyleSheets();
|
||||
getStyleSheet() {
|
||||
// reset state on the server so critical css is always the result
|
||||
const sheet = this.styleSheetManager.getStyleSheet();
|
||||
if (!canUseDOM) {
|
||||
this._init();
|
||||
}
|
||||
return sheet;
|
||||
}
|
||||
|
||||
_injectRegisteredStyle(id) {
|
||||
@@ -48,27 +59,27 @@ export default class ReactNativeStyleResolver {
|
||||
/**
|
||||
* Resolves a React Native style object to DOM attributes
|
||||
*/
|
||||
resolve(reactNativeStyle) {
|
||||
if (!reactNativeStyle) {
|
||||
resolve(style) {
|
||||
if (!style) {
|
||||
return emptyObject;
|
||||
}
|
||||
|
||||
// fast and cachable
|
||||
if (typeof reactNativeStyle === 'number') {
|
||||
this._injectRegisteredStyle(reactNativeStyle);
|
||||
const key = createCacheKey(reactNativeStyle);
|
||||
return this._resolveStyleIfNeeded(reactNativeStyle, key);
|
||||
if (typeof style === 'number') {
|
||||
this._injectRegisteredStyle(style);
|
||||
const key = createCacheKey(style);
|
||||
return this._resolveStyleIfNeeded(style, key);
|
||||
}
|
||||
|
||||
// resolve a plain RN style object
|
||||
if (!Array.isArray(reactNativeStyle)) {
|
||||
return this._resolveStyleIfNeeded(reactNativeStyle);
|
||||
if (!Array.isArray(style)) {
|
||||
return this._resolveStyleIfNeeded(style);
|
||||
}
|
||||
|
||||
// flatten the style array
|
||||
// cache resolved props when all styles are registered
|
||||
// otherwise fallback to resolving
|
||||
const flatArray = flattenArray(reactNativeStyle);
|
||||
const flatArray = flattenArray(style);
|
||||
let isArrayOfNumbers = true;
|
||||
for (let i = 0; i < flatArray.length; i++) {
|
||||
const id = flatArray[i];
|
||||
@@ -134,8 +145,8 @@ export default class ReactNativeStyleResolver {
|
||||
/**
|
||||
* Resolves a React Native style object
|
||||
*/
|
||||
_resolveStyle(reactNativeStyle) {
|
||||
const flatStyle = flattenStyle(reactNativeStyle);
|
||||
_resolveStyle(style) {
|
||||
const flatStyle = flattenStyle(style);
|
||||
const domStyle = createReactDOMStyle(i18nStyle(flatStyle));
|
||||
|
||||
const props = Object.keys(domStyle).reduce(
|
||||
|
||||
@@ -27,8 +27,8 @@ export default class StyleSheetManager {
|
||||
byProp: {}
|
||||
};
|
||||
|
||||
constructor(id) {
|
||||
this._id = this._sheet = new WebStyleSheet(STYLE_ELEMENT_ID);
|
||||
constructor() {
|
||||
this._sheet = new WebStyleSheet(STYLE_ELEMENT_ID);
|
||||
initialRules.forEach(rule => {
|
||||
this._sheet.insertRuleOnce(rule);
|
||||
});
|
||||
@@ -44,15 +44,13 @@ export default class StyleSheetManager {
|
||||
return cache[className] || emptyObject;
|
||||
}
|
||||
|
||||
getStyleSheets() {
|
||||
getStyleSheet() {
|
||||
const { cssText } = this._sheet;
|
||||
|
||||
return [
|
||||
{
|
||||
id: STYLE_ELEMENT_ID,
|
||||
textContent: cssText
|
||||
}
|
||||
];
|
||||
return {
|
||||
id: STYLE_ELEMENT_ID,
|
||||
textContent: cssText
|
||||
};
|
||||
}
|
||||
|
||||
injectDeclaration(prop, value): string {
|
||||
|
||||
@@ -12,25 +12,29 @@ import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
|
||||
|
||||
export default class WebStyleSheet {
|
||||
_cssRules = [];
|
||||
_domStyleElement = null;
|
||||
_sheet = null;
|
||||
_textContent = '';
|
||||
|
||||
constructor(id: string) {
|
||||
let _domStyleElement;
|
||||
let domStyleElement;
|
||||
|
||||
// on the client we check for an existing style sheet before injecting
|
||||
if (canUseDOM) {
|
||||
_domStyleElement = document.getElementById(id);
|
||||
if (!_domStyleElement) {
|
||||
domStyleElement = document.getElementById(id);
|
||||
if (!domStyleElement) {
|
||||
const html = `<style id="${id}"></style>`;
|
||||
if (document.head) {
|
||||
document.head.insertAdjacentHTML('afterbegin', html);
|
||||
_domStyleElement = document.getElementById(id);
|
||||
domStyleElement = document.getElementById(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
this._domStyleElement = _domStyleElement;
|
||||
if (domStyleElement) {
|
||||
// $FlowFixMe
|
||||
this._sheet = domStyleElement.sheet;
|
||||
this._textContent = domStyleElement.textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
containsRule(rule: string): boolean {
|
||||
@@ -42,21 +46,15 @@ export default class WebStyleSheet {
|
||||
}
|
||||
|
||||
insertRuleOnce(rule: string, position: ?number) {
|
||||
// prevent duplicate rules
|
||||
// Reduce chance of duplicate rules
|
||||
if (!this.containsRule(rule)) {
|
||||
this._cssRules.push(rule);
|
||||
|
||||
// update the native stylesheet (i.e., browser)
|
||||
if (this._domStyleElement) {
|
||||
// Check whether a rule was part of any prerendered styles (textContent
|
||||
// doesn't include styles injected via 'insertRule')
|
||||
if (this._domStyleElement.textContent.indexOf(rule) === -1) {
|
||||
// $FlowFixMe
|
||||
this._domStyleElement.sheet.insertRule(
|
||||
rule,
|
||||
position || this._domStyleElement.sheet.cssRules.length
|
||||
);
|
||||
}
|
||||
// Check whether a rule was part of any prerendered styles (textContent
|
||||
// doesn't include styles injected via 'insertRule')
|
||||
if (this._textContent.indexOf(rule) === -1 && this._sheet) {
|
||||
const pos = position || this._sheet.cssRules.length;
|
||||
this._sheet.insertRule(rule, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,9 +23,9 @@ describe('StyleSheet/StyleSheetManager', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('getStyleSheets', () => {
|
||||
test('getStyleSheet', () => {
|
||||
styleSheetManager.injectDeclaration('--test-property', 'test-value');
|
||||
expect(styleSheetManager.getStyleSheets()).toMatchSnapshot();
|
||||
expect(styleSheetManager.getStyleSheet()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('injectDeclaration', () => {
|
||||
|
||||
+5
-7
@@ -2,11 +2,10 @@
|
||||
|
||||
exports[`StyleSheet/StyleSheetManager getClassName 1`] = `undefined`;
|
||||
|
||||
exports[`StyleSheet/StyleSheetManager getStyleSheets 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"id": "react-native-stylesheet",
|
||||
"textContent": "@media all{
|
||||
exports[`StyleSheet/StyleSheetManager getStyleSheet 1`] = `
|
||||
Object {
|
||||
"id": "react-native-stylesheet",
|
||||
"textContent": "@media all{
|
||||
html{-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;}
|
||||
@@ -15,6 +14,5 @@ input::-webkit-inner-spin-button,input::-webkit-outer-spin-button,input::-webkit
|
||||
@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---test-property-ax3bxi{--test-property:test-value}",
|
||||
},
|
||||
]
|
||||
}
|
||||
`;
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`StyleSheet getStyleSheets 1`] = `
|
||||
Array [
|
||||
Object {
|
||||
"id": "react-native-stylesheet",
|
||||
"textContent": "@media all{
|
||||
html{-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%);}}",
|
||||
},
|
||||
]
|
||||
`;
|
||||
@@ -48,8 +48,4 @@ describe('StyleSheet', () => {
|
||||
test('hairlineWidth', () => {
|
||||
expect(Number.isInteger(StyleSheet.hairlineWidth) === true).toBeTruthy();
|
||||
});
|
||||
|
||||
test('getStyleSheets', () => {
|
||||
expect(StyleSheet.getStyleSheets()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -56,7 +56,7 @@ otherwise it is not recommended.
|
||||
|
||||
## Server-side rendering
|
||||
|
||||
Server-side rendering is supported using `AppRegistry`:
|
||||
Server-side rendering to HTML is supported using `AppRegistry`:
|
||||
|
||||
```js
|
||||
import App from './src/App';
|
||||
@@ -67,19 +67,20 @@ import { AppRegistry } from 'react-native'
|
||||
AppRegistry.registerComponent('App', () => App)
|
||||
|
||||
// prerender the app
|
||||
const { element, stylesheets } = AppRegistry.getApplication('App', { initialProps });
|
||||
const initialHTML = ReactDOMServer.renderToString(element);
|
||||
const initialStyles = stylesheets.map((sheet) => ReactDOMServer.renderToStaticMarkup(sheet)).join('\n');
|
||||
const { element, getStyleElement } = AppRegistry.getApplication('App', { initialProps });
|
||||
// first the element
|
||||
const html = ReactDOMServer.renderToString(element);
|
||||
// then the styles
|
||||
const css = ReactDOMServer.renderToStaticMarkup(getStyleElement());
|
||||
|
||||
// construct HTML document string
|
||||
// example HTML document string
|
||||
const document = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
${initialStyles}
|
||||
</head>
|
||||
<body>
|
||||
${initialHTML}
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
${css}
|
||||
${html}
|
||||
`
|
||||
```
|
||||
|
||||
|
||||
@@ -24,13 +24,6 @@ const AppRegistryScreen = () => (
|
||||
</Description>
|
||||
|
||||
<Section title="Methods">
|
||||
<DocItem
|
||||
description="Returns the given application's element and stylesheets. Use this for server-side rendering."
|
||||
label="web"
|
||||
name="static getApplication"
|
||||
typeInfo="(appKey: string, appParameters: ?object) => { element: ReactElement; stylesheets: Array<ReactElement> }"
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
description={[
|
||||
<AppText>
|
||||
@@ -105,6 +98,13 @@ const AppRegistryScreen = () => (
|
||||
name="static unmountApplicationComponentAtRootTag"
|
||||
typeInfo="(rootTag: HTMLElement) => void"
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
description="Use this for server-side rendering to HTML. Returns a object of the given application's element, and a function to get styles once the element is rendered."
|
||||
label="web"
|
||||
name="static getApplication"
|
||||
typeInfo="(appKey: string, appParameters: ?object) => { element: ReactElement; getStyleElement: () => ReactElement }"
|
||||
/>
|
||||
</Section>
|
||||
</UIExplorer>
|
||||
);
|
||||
|
||||
@@ -65,18 +65,6 @@ StyleSheet.flatten([styles.listItem, styles.selectedListItem]);`
|
||||
name="flatten"
|
||||
typeInfo="()"
|
||||
/>
|
||||
|
||||
<DocItem
|
||||
description={
|
||||
<AppText>
|
||||
Returns an array of stylesheets of the form <Code>{'{ id, textContent }'}</Code>. Useful
|
||||
for compile-time or server-side rendering if you are not using AppRegistry.
|
||||
</AppText>
|
||||
}
|
||||
label="web"
|
||||
name="getStyleSheets"
|
||||
typeInfo="() => Array"
|
||||
/>
|
||||
</Section>
|
||||
|
||||
<Section title="Properties">
|
||||
|
||||
Reference in New Issue
Block a user