@samitouri / QOS-React-2 / commits / a352b94f57

Give context aware message if markup reuse fails

The context is an extra message with comparison of where server and client markup started to differ.

Árni Hermann Reynisson committed Nov 30, 2014 at 16:27 UTC a352b94f57150e0e84fd1d92f1aebb7d5f422890
2 files changed +40 -6
src/browser/ui/ReactMount.js
+37 -5
@@ -54,6 +54,22 @@ if (__DEV__) {
54 // Used to store breadth-first search state in findComponentRoot.
55 var findComponentRootReusableArray = [];
56
57 +/**
58 + * Finds the index of the first character
59 + * that's not common between the two given strings.
60 + *
61 + * @return {number} the index of the character where the strings diverge
62 + */
63 +function firstDifferenceIndex(string1, string2) {
64 + var minLen = Math.min(string1.length, string2.length);
65 + for (var i = 0; i < minLen; i++) {
66 + if (string1[i] !== string2[i]) {
67 + return i;
68 + }
69 + }
70 + return string1.length === string2.length ? -1 : minLen;
71 +}
72 +
73 /**
74 * @param {DOMElement} container DOM element that may contain a React component.
75 * @return {?string} A "reactRoot" ID, if a React component is rendered.
@@ -719,11 +735,26 @@ var ReactMount = {
735 );
736
737 if (shouldReuseMarkup) {
722 - if (ReactMarkupChecksum.canReuseMarkup(
723 - markup,
724 - getReactRootElementInContainer(container))) {
738 + var rootElement = getReactRootElementInContainer(container);
739 + if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
740 return;
741 } else {
742 + var checksum = rootElement.getAttribute(
743 + ReactMarkupChecksum.CHECKSUM_ATTR_NAME
744 + );
745 + rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);
746 +
747 + var rootMarkup = rootElement.outerHTML;
748 + rootElement.setAttribute(
749 + ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
750 + checksum
751 + );
752 +
753 + var diffIndex = firstDifferenceIndex(markup, rootMarkup);
754 + var difference = ' (client) ' +
755 + markup.substring(diffIndex - 20, diffIndex + 20) +
756 + '\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);
757 +
758 invariant(
759 container.nodeType !== DOC_NODE_TYPE,
760 'You\'re trying to render a component to the document using ' +
@@ -733,7 +764,8 @@ var ReactMount = {
764 'methods are impure. React cannot handle this case due to ' +
765 'cross-browser quirks by rendering at the document root. You ' +
766 'should look for environment dependent code in your components ' +
736 - 'and ensure the props are the same client and server side.'
767 + 'and ensure the props are the same client and server side:\n' +
768 + difference
769 );
770
771 if (__DEV__) {
@@ -745,7 +777,7 @@ var ReactMount = {
777 'new markup to compensate which works but you have lost many ' +
778 'of the benefits of server rendering. Instead, figure out ' +
779 'why the markup being generated is different on the client ' +
748 - 'or server.'
780 + 'or server:\n' + difference
781 );
782 }
783 }
src/browser/ui/__tests__/ReactRenderDocument-test.js
+3 -1
@@ -210,7 +210,9 @@ describe('rendering React components at document', function() {
210 'are impure. React cannot handle this case due to cross-browser ' +
211 'quirks by rendering at the document root. You should look for ' +
212 'environment dependent code in your components and ensure ' +
213 - 'the props are the same client and server side.'
213 + 'the props are the same client and server side:\n' +
214 + ' (client) data-reactid=".0.1">Hello world</body></\n' +
215 + ' (server) data-reactid=".0.1">Goodbye world</body>'
216 );
217 });
218