@samitouri / QOS-React / commits / e6f1d29f07

Warn for inline style mismatches (#10084)

I use the technique of generating a style string and comparing that against the attribute.

Sebastian Markbåge committed Jun 30, 2017 at 18:24 UTC e6f1d29f072b2be2e9795ffc781a965cb69f0347
5 files changed +87 -5
scripts/fiber/tests-passing.txt
+7
@@ -1531,6 +1531,13 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1531 * should error reconnecting missing attributes
1532 * should error reconnecting added attributes
1533 * should error reconnecting different attribute values
1534 +* should error reconnecting missing style attribute
1535 +* should error reconnecting added style attribute
1536 +* should error reconnecting empty style attribute
1537 +* should error reconnecting added style values
1538 +* should error reconnecting different style values
1539 +* should reconnect number and string versions of a number
1540 +* should error reconnecting reordered style values
1541 * should error reconnecting different text
1542 * should reconnect a div with a number and string version of number
1543 * should error reconnecting different numbers
src/renderers/dom/fiber/ReactDOMFiberComponent.js
+7 -1
@@ -915,7 +915,13 @@ var ReactDOMFiberComponent = {
915 } else if (propKey === STYLE) {
916 // $FlowFixMe - Should be inferred as not undefined.
917 extraAttributeNames.delete(propKey);
918 - // TOOD: Validate style sheets.
918 + const expectedStyle = CSSPropertyOperations.createDangerousStringForStyles(
919 + nextProp,
920 + );
921 + serverValue = domElement.getAttribute('style');
922 + if (expectedStyle !== serverValue) {
923 + warnForPropDifference(propKey, serverValue, expectedStyle);
924 + }
925 } else if (
926 isCustomComponentTag ||
927 DOMProperty.isCustomAttribute(propKey)
src/renderers/dom/shared/CSSPropertyOperations.js
+34 -2
@@ -207,7 +207,6 @@ var CSSPropertyOperations = {
207 serialized += dangerousStyleValue(
208 styleName,
209 styleValue,
210 - component,
210 isCustomProperty,
211 );
212
@@ -217,6 +216,40 @@ var CSSPropertyOperations = {
216 return serialized || null;
217 },
218
219 + /**
220 + * This creates a string that is expected to be equivalent to the style
221 + * attribute generated by server-side rendering. It by-passes warnings and
222 + * security checks so it's not safe to use this value for anything other than
223 + * comparison. It is only used in DEV for SSR validation. This is duplicated
224 + * from createMarkupForStyles because createMarkupForStyles is expected to
225 + * move out of the client-side renderer and it would be nice to make a clean
226 + * break.
227 + */
228 + createDangerousStringForStyles: function(styles) {
229 + if (__DEV__) {
230 + var serialized = '';
231 + var delimiter = '';
232 + for (var styleName in styles) {
233 + if (!styles.hasOwnProperty(styleName)) {
234 + continue;
235 + }
236 + var styleValue = styles[styleName];
237 + if (styleValue != null) {
238 + var isCustomProperty = styleName.indexOf('--') === 0;
239 + serialized += delimiter + hyphenateStyleName(styleName) + ':';
240 + serialized += dangerousStyleValue(
241 + styleName,
242 + styleValue,
243 + isCustomProperty,
244 + );
245 +
246 + delimiter = ';';
247 + }
248 + }
249 + return serialized || null;
250 + }
251 + },
252 +
253 /**
254 * Sets the value for multiple styles on a node. If a value is specified as
255 * '' (empty string), the corresponding style property will be unset.
@@ -240,7 +273,6 @@ var CSSPropertyOperations = {
273 var styleValue = dangerousStyleValue(
274 styleName,
275 styles[styleName],
243 - component,
276 isCustomProperty,
277 );
278 if (styleName === 'float') {
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+38
@@ -2075,6 +2075,44 @@ describe('ReactDOMServerIntegration', () => {
2075 expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
2076 });
2077
2078 + describe('inline styles', function() {
2079 + it('should error reconnecting missing style attribute', () =>
2080 + expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
2081 +
2082 + it('should error reconnecting added style attribute', () =>
2083 + expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
2084 +
2085 + it('should error reconnecting empty style attribute', () =>
2086 + expectMarkupMismatch(
2087 + <div style={{width: '1px'}} />,
2088 + <div style={{}} />,
2089 + ));
2090 +
2091 + it('should error reconnecting added style values', () =>
2092 + expectMarkupMismatch(
2093 + <div style={{}} />,
2094 + <div style={{width: '1px'}} />,
2095 + ));
2096 +
2097 + it('should error reconnecting different style values', () =>
2098 + expectMarkupMismatch(
2099 + <div style={{width: '1px'}} />,
2100 + <div style={{width: '2px'}} />,
2101 + ));
2102 +
2103 + it('should reconnect number and string versions of a number', () =>
2104 + expectMarkupMatch(
2105 + <div style={{width: '1px', height: 2}} />,
2106 + <div style={{width: 1, height: '2px'}} />,
2107 + ));
2108 +
2109 + it('should error reconnecting reordered style values', () =>
2110 + expectMarkupMismatch(
2111 + <div style={{width: '1px', fontSize: '2px'}} />,
2112 + <div style={{fontSize: '2px', width: '1px'}} />,
2113 + ));
2114 + });
2115 +
2116 describe('text nodes', function() {
2117 it('should error reconnecting different text', () =>
2118 expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
src/renderers/dom/shared/dangerousStyleValue.js
+1 -2
@@ -22,10 +22,9 @@ var isUnitlessNumber = CSSProperty.isUnitlessNumber;
22 *
23 * @param {string} name CSS property name such as `topMargin`.
24 * @param {*} value CSS property value such as `10px`.
25 - * @param {ReactDOMComponent} component
25 * @return {string} Normalized style value with dimensions applied.
26 */
28 -function dangerousStyleValue(name, value, component, isCustomProperty) {
27 +function dangerousStyleValue(name, value, isCustomProperty) {
28 // Note that we've removed escapeTextForBrowser() calls here since the
29 // whole string will be escaped when the attribute is injected into
30 // the markup. If you provide unsafe user data here they can inject