Skip to content

Commit

Permalink
Give context aware message if markup reuse fails
Browse files Browse the repository at this point in the history
The context is an extra message with comparison of where server and
client markup started to differ.
  • Loading branch information
arnihermann committed Dec 1, 2014
1 parent 5bd0b24 commit a352b94
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 6 deletions.
42 changes: 37 additions & 5 deletions src/browser/ui/ReactMount.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@ if (__DEV__) {
// Used to store breadth-first search state in findComponentRoot.
var findComponentRootReusableArray = [];

/**
* Finds the index of the first character
* that's not common between the two given strings.
*
* @return {number} the index of the character where the strings diverge
*/
function firstDifferenceIndex(string1, string2) {
var minLen = Math.min(string1.length, string2.length);
for (var i = 0; i < minLen; i++) {

This comment has been minimized.

Copy link
@plievone

plievone Dec 3, 2014

Contributor

@vjeux @arnihermann Does react codebase use generally .charAt() instead?

This comment has been minimized.

Copy link
@arnihermann

arnihermann Dec 3, 2014

Author Contributor

@plievone Good catch. I've submitted another pull request to fix, #2651

if (string1[i] !== string2[i]) {
return i;
}
}
return string1.length === string2.length ? -1 : minLen;
}

/**
* @param {DOMElement} container DOM element that may contain a React component.
* @return {?string} A "reactRoot" ID, if a React component is rendered.
Expand Down Expand Up @@ -719,11 +735,26 @@ var ReactMount = {
);

if (shouldReuseMarkup) {
if (ReactMarkupChecksum.canReuseMarkup(
markup,
getReactRootElementInContainer(container))) {
var rootElement = getReactRootElementInContainer(container);
if (ReactMarkupChecksum.canReuseMarkup(markup, rootElement)) {
return;
} else {
var checksum = rootElement.getAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME
);
rootElement.removeAttribute(ReactMarkupChecksum.CHECKSUM_ATTR_NAME);

var rootMarkup = rootElement.outerHTML;
rootElement.setAttribute(
ReactMarkupChecksum.CHECKSUM_ATTR_NAME,
checksum
);

var diffIndex = firstDifferenceIndex(markup, rootMarkup);
var difference = ' (client) ' +
markup.substring(diffIndex - 20, diffIndex + 20) +
'\n (server) ' + rootMarkup.substring(diffIndex - 20, diffIndex + 20);

invariant(
container.nodeType !== DOC_NODE_TYPE,
'You\'re trying to render a component to the document using ' +
Expand All @@ -733,7 +764,8 @@ var ReactMount = {
'methods are impure. React cannot handle this case due to ' +
'cross-browser quirks by rendering at the document root. You ' +
'should look for environment dependent code in your components ' +
'and ensure the props are the same client and server side.'
'and ensure the props are the same client and server side:\n' +
difference
);

if (__DEV__) {
Expand All @@ -745,7 +777,7 @@ var ReactMount = {
'new markup to compensate which works but you have lost many ' +
'of the benefits of server rendering. Instead, figure out ' +
'why the markup being generated is different on the client ' +
'or server.'
'or server:\n' + difference
);
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/browser/ui/__tests__/ReactRenderDocument-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,9 @@ describe('rendering React components at document', function() {
'are impure. React cannot handle this case due to cross-browser ' +
'quirks by rendering at the document root. You should look for ' +
'environment dependent code in your components and ensure ' +
'the props are the same client and server side.'
'the props are the same client and server side:\n' +
' (client) data-reactid=".0.1">Hello world</body></\n' +
' (server) data-reactid=".0.1">Goodbye world</body>'
);
});

Expand Down

0 comments on commit a352b94

Please sign in to comment.