Skip to content

Commit

Permalink
Deprecate ReactDOM.render() hydration in favor of ReactDOM.hydrate()
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed Aug 1, 2017
1 parent 462e018 commit 6ac1772
Show file tree
Hide file tree
Showing 9 changed files with 670 additions and 226 deletions.
25 changes: 24 additions & 1 deletion src/renderers/__tests__/ReactCompositeComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var ChildUpdates;
var MorphingComponent;
var React;
var ReactDOM;
var ReactDOMFeatureFlags;
var ReactDOMServer;
var ReactCurrentOwner;
var ReactTestUtils;
Expand All @@ -27,6 +28,7 @@ describe('ReactCompositeComponent', () => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactDOMServer = require('react-dom/server');
ReactCurrentOwner = require('react')
.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;
Expand Down Expand Up @@ -116,11 +118,32 @@ describe('ReactCompositeComponent', () => {
}
}

spyOn(console, 'error');
var markup = ReactDOMServer.renderToString(<Parent />);

// Old API based on heuristic
var container = document.createElement('div');
container.innerHTML = markup;

ReactDOM.render(<Parent />, container);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'has been deprecated as of React v16. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
} else {
expectDev(console.error.calls.count()).toBe(0);
}

// New explicit API
console.error.calls.reset();
if (ReactDOMFeatureFlags.useFiber) {
container = document.createElement('div');
container.innerHTML = markup;
ReactDOM.hydrate(<Parent />, container);
expectDev(console.error.calls.count()).toBe(0);
}
});

it('should react to state changes from callbacks', () => {
Expand Down
14 changes: 13 additions & 1 deletion src/renderers/dom/fiber/ReactDOMFiberEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ ReactGenericBatching.injection.injectFiberBatchedUpdates(
DOMRenderer.batchedUpdates,
);

var warnedAboutHydrateAPI = false;

function renderSubtreeIntoContainer(
parentComponent: ?ReactComponent<any, any, any>,
children: ReactNodeList,
Expand Down Expand Up @@ -581,7 +583,6 @@ function renderSubtreeIntoContainer(
const shouldHydrate =
forceHydrate || shouldHydrateDueToLegacyHeuristic(container);
// First clear any existing content.
// TODO: warn if we're hydrating based on heuristic and suggest using hydrate().
if (!shouldHydrate) {
let warned = false;
let rootSibling;
Expand All @@ -604,6 +605,17 @@ function renderSubtreeIntoContainer(
container.removeChild(rootSibling);
}
}
if (__DEV__) {
if (shouldHydrate && !forceHydrate && !warnedAboutHydrateAPI) {
warnedAboutHydrateAPI = true;
warning(
false,
'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
'has been deprecated as of React v16. Replace the ReactDOM.render() call ' +
'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
);
}
}
const newRoot = DOMRenderer.createContainer(container);
root = container._reactRootContainer = newRoot;
// Initial mount should not be batched.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

'use strict';

var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');

describe('ReactDOMComponentTree', () => {
var React;
var ReactDOM;
Expand All @@ -21,7 +23,11 @@ describe('ReactDOMComponentTree', () => {
var container = document.createElement('div');
// Force server-rendering path:
container.innerHTML = ReactDOMServer.renderToString(elt);
return ReactDOM.render(elt, container);
if (ReactDOMFeatureFlags.useFiber) {
return ReactDOM.hydrate(elt, container);
} else {
return ReactDOM.render(elt, container);
}
}

function getTypeOf(instance) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1972,7 +1972,11 @@ describe('ReactDOMServerIntegration', () => {

resetModules();
// client render on top of the server markup.
const clientField = await renderIntoDom(element, field.parentNode);
const clientField = await renderIntoDom(
element,
field.parentNode,
true,
);
// verify that the input field was not replaced.
// Note that we cannot use expect(clientField).toBe(field) because
// of jest bug #1772
Expand Down Expand Up @@ -2331,6 +2335,7 @@ describe('ReactDOMServerIntegration', () => {
await asyncReactDOMRender(
<RefsComponent ref={e => (component = e)} />,
root,
true,
);
expect(component.refs.myDiv).toBe(root.firstChild);
});
Expand Down
14 changes: 12 additions & 2 deletions src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
var React;
var ReactDOM;
var ReactDOMServer;
var ReactDOMFeatureFlags;

// In standard React, TextComponent keeps track of different Text templates
// using comments. However, in React Fiber, those comments are not outputted due
Expand All @@ -29,6 +30,7 @@ describe('ReactDOMTextComponent', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
});

it('updates a mounted text component in place', () => {
Expand Down Expand Up @@ -117,15 +119,23 @@ describe('ReactDOMTextComponent', () => {
var reactEl = <div>{'foo'}{'bar'}{'baz'}</div>;
el.innerHTML = ReactDOMServer.renderToString(reactEl);

ReactDOM.render(reactEl, el);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(reactEl, el);
} else {
ReactDOM.render(reactEl, el);
}
expect(el.textContent).toBe('foobarbaz');

ReactDOM.unmountComponentAtNode(el);

reactEl = <div>{''}{''}{''}</div>;
el.innerHTML = ReactDOMServer.renderToString(reactEl);

ReactDOM.render(reactEl, el);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(reactEl, el);
} else {
ReactDOM.render(reactEl, el);
}
expect(el.textContent).toBe('');
});

Expand Down
48 changes: 36 additions & 12 deletions src/renderers/dom/shared/__tests__/ReactMount-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,16 @@ describe('ReactMount', () => {
expect(instance1 === instance2).toBe(true);
});

it('should warn if mounting into dirty rendered markup', () => {
it('should warn if mounting into left padded rendered markup', () => {
var container = document.createElement('container');
container.innerHTML = ReactDOMServer.renderToString(<div />) + ' ';

spyOn(console, 'error');
ReactDOM.render(<div />, container);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(<div />, container);
} else {
ReactDOM.render(<div />, container);
}
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
Expand All @@ -162,15 +166,28 @@ describe('ReactMount', () => {
'Target node has markup rendered by React, but there are unrelated nodes as well.',
);
}
});

console.error.calls.reset();
ReactDOM.unmountComponentAtNode(container);
it('should warn if mounting into right padded rendered markup', () => {
var container = document.createElement('container');
container.innerHTML = ' ' + ReactDOMServer.renderToString(<div />);
ReactDOM.render(<div />, container);

spyOn(console, 'error');
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(<div />, container);
} else {
ReactDOM.render(<div />, container);
}
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Target node has markup rendered by React, but there are unrelated nodes as well.',
);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node " " in <container>.',
);
} else {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Target node has markup rendered by React, but there are unrelated nodes as well.',
);
}
});

it('should not warn if mounting into non-empty node', () => {
Expand Down Expand Up @@ -203,10 +220,17 @@ describe('ReactMount', () => {
div.innerHTML = markup;

spyOn(console, 'error');
ReactDOM.render(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
} else {
ReactDOM.render(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
}
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
Expand Down
Loading

0 comments on commit 6ac1772

Please sign in to comment.