main
js 201 lines 5.11 KB
Raw
1 /**
2 * Supports render.html, a piece of the hydration fixture. See /hydration
3 */
4
5 'use strict';
6
7 (function () {
8 var Fixture = null;
9 var output = document.getElementById('output');
10 var status = document.getElementById('status');
11 var hydrate = document.getElementById('hydrate');
12 var reload = document.getElementById('reload');
13 var renders = 0;
14 var failed = false;
15
16 var needsReactDOM = getBooleanQueryParam('needsReactDOM');
17 var needsCreateElement = getBooleanQueryParam('needsCreateElement');
18
19 function unmountComponent(node) {
20 // ReactDOM was moved into a separate package in 0.14
21 if (needsReactDOM) {
22 ReactDOM.unmountComponentAtNode(node);
23 } else if (React.unmountComponentAtNode) {
24 React.unmountComponentAtNode(node);
25 } else {
26 // Unmounting for React 0.4 and lower
27 React.unmountAndReleaseReactRootNode(node);
28 }
29 }
30
31 function createElement(value) {
32 // React.createElement replaced function invocation in 0.12
33 if (needsCreateElement) {
34 return React.createElement(value);
35 } else {
36 return value();
37 }
38 }
39
40 function getQueryParam(key) {
41 var pattern = new RegExp(key + '=([^&]+)(&|$)');
42 var matches = window.location.search.match(pattern);
43
44 if (matches) {
45 return decodeURIComponent(matches[1]);
46 }
47
48 handleError(new Error('No key found for' + key));
49 }
50
51 function getBooleanQueryParam(key) {
52 return getQueryParam(key) === 'true';
53 }
54
55 function setStatus(label) {
56 status.innerHTML = label;
57 }
58
59 function prerender() {
60 setStatus('Generating markup');
61
62 return Promise.resolve()
63 .then(function () {
64 const element = createElement(Fixture);
65
66 // Server rendering moved to a separate package along with ReactDOM
67 // in 0.14.0
68 if (needsReactDOM) {
69 return ReactDOMServer.renderToString(element);
70 }
71
72 // React.renderComponentToString was renamed in 0.12
73 if (React.renderToString) {
74 return React.renderToString(element);
75 }
76
77 // React.renderComponentToString became synchronous in React 0.9.0
78 if (React.renderComponentToString.length === 1) {
79 return React.renderComponentToString(element);
80 }
81
82 // Finally, React 0.4 and lower emits markup in a callback
83 return new Promise(function (resolve) {
84 React.renderComponentToString(element, resolve);
85 });
86 })
87 .then(function (string) {
88 output.innerHTML = string;
89 setStatus('Markup only (No React)');
90 })
91 .catch(handleError);
92 }
93
94 function render() {
95 setStatus('Hydrating');
96
97 var element = createElement(Fixture);
98
99 // ReactDOM was split out into another package in 0.14
100 if (needsReactDOM) {
101 // Hydration changed to a separate method in React 16
102 if (ReactDOM.hydrate) {
103 ReactDOM.hydrate(element, output);
104 } else {
105 ReactDOM.render(element, output);
106 }
107 } else if (React.render) {
108 // React.renderComponent was renamed in 0.12
109 React.render(element, output);
110 } else {
111 React.renderComponent(element, output);
112 }
113
114 setStatus(renders > 0 ? 'Re-rendered (' + renders + 'x)' : 'Hydrated');
115 renders += 1;
116 hydrate.innerHTML = 'Re-render';
117 }
118
119 function handleError(error) {
120 console.log(error);
121 failed = true;
122 setStatus('Javascript Error');
123 output.innerHTML = error;
124 }
125
126 function loadScript(src) {
127 return new Promise(function (resolve, reject) {
128 var script = document.createElement('script');
129 script.async = true;
130 script.src = src;
131
132 script.onload = resolve;
133 script.onerror = function (error) {
134 reject(new Error('Unable to load ' + src));
135 };
136
137 document.body.appendChild(script);
138 });
139 }
140
141 function injectFixture(src) {
142 Fixture = new Function(src + '\nreturn Fixture;')();
143
144 if (typeof Fixture === 'undefined') {
145 setStatus('Failed');
146 output.innerHTML = 'Please name your root component "Fixture"';
147 } else {
148 prerender().then(function () {
149 if (getBooleanQueryParam('hydrate')) {
150 render();
151 }
152 });
153 }
154 }
155
156 function reloadFixture(code) {
157 renders = 0;
158 unmountComponent(output);
159 injectFixture(code);
160 }
161
162 window.onerror = handleError;
163
164 reload.onclick = function () {
165 window.location.reload();
166 };
167
168 hydrate.onclick = render;
169
170 loadScript(getQueryParam('reactPath'))
171 .then(function () {
172 if (needsReactDOM) {
173 return Promise.all([
174 loadScript(getQueryParam('reactDOMPath')),
175 loadScript(getQueryParam('reactDOMServerPath')),
176 ]);
177 }
178 })
179 .then(function () {
180 if (failed) {
181 return;
182 }
183
184 window.addEventListener('message', function (event) {
185 var data = JSON.parse(event.data);
186
187 switch (data.type) {
188 case 'code':
189 reloadFixture(data.payload);
190 break;
191 default:
192 throw new Error(
193 'Renderer Error: Unrecognized message "' + data.type + '"'
194 );
195 }
196 });
197
198 window.parent.postMessage(JSON.stringify({type: 'ready'}), '*');
199 })
200 .catch(handleError);
201 })();