diff --git a/packages/material-ui/src/NoSsr/NoSsr.js b/packages/material-ui/src/NoSsr/NoSsr.js
index dd33fe28e596e9..47666bd8b310ff 100644
--- a/packages/material-ui/src/NoSsr/NoSsr.js
+++ b/packages/material-ui/src/NoSsr/NoSsr.js
@@ -2,8 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import exactProp from '../utils/exactProp';
-const Fallback = () => null;
-
/**
* NoSsr purposely removes components from the subject of Server Side Rendering (SSR).
*
@@ -19,7 +17,27 @@ class NoSsr extends React.Component {
};
componentDidMount() {
- this.setState({ mounted: true }); // eslint-disable-line react/no-did-mount-set-state
+ this.mounted = true;
+
+ if (this.props.defer) {
+ // Wondering why we use two raf? Check this video out:
+ // https://www.youtube.com/watch?v=cCOL7MC4Pl0
+ requestAnimationFrame(() => {
+ // The browser should be about to render the DOM that React commited at this point.
+ // We don't want to interrupt. Let's wait the next raf.
+ requestAnimationFrame(() => {
+ if (this.mounted) {
+ this.setState({ mounted: true });
+ }
+ });
+ });
+ } else {
+ this.setState({ mounted: true }); // eslint-disable-line react/no-did-mount-set-state
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
}
render() {
@@ -31,13 +49,22 @@ class NoSsr extends React.Component {
NoSsr.propTypes = {
children: PropTypes.node.isRequired,
+ /**
+ * If `true`, the component will not only prevent server side rendering.
+ * It will also defer the rendering of the children into a different screen frame.
+ */
+ defer: PropTypes.bool,
+ /**
+ * The fallback content to display.
+ */
fallback: PropTypes.node,
};
NoSsr.propTypes = exactProp(NoSsr.propTypes);
NoSsr.defaultProps = {
- fallback: ,
+ defer: false,
+ fallback: null,
};
export default NoSsr;
diff --git a/packages/material-ui/src/NoSsr/NoSsr.test.js b/packages/material-ui/src/NoSsr/NoSsr.test.js
index 54cb8ef08d412c..b99f71aea3555e 100644
--- a/packages/material-ui/src/NoSsr/NoSsr.test.js
+++ b/packages/material-ui/src/NoSsr/NoSsr.test.js
@@ -25,7 +25,7 @@ describe('', () => {
Hello
,
);
- assert.strictEqual(wrapper.name(), 'Fallback');
+ assert.strictEqual(wrapper.name(), null);
});
});
@@ -37,6 +37,7 @@ describe('', () => {
,
);
assert.strictEqual(wrapper.find('span').length, 1);
+ assert.strictEqual(wrapper.text(), 'Hello');
});
});
@@ -50,4 +51,21 @@ describe('', () => {
assert.strictEqual(wrapper.text(), 'fallback');
});
});
+
+ describe('prop: defer', () => {
+ it('should defer the rendering', done => {
+ const wrapper = mount(
+
+ Hello
+ ,
+ );
+ assert.strictEqual(wrapper.find('span').length, 0);
+ setTimeout(() => {
+ wrapper.update();
+ assert.strictEqual(wrapper.find('span').length, 1);
+ assert.strictEqual(wrapper.text(), 'Hello');
+ done();
+ }, 100);
+ });
+ });
});
diff --git a/pages/api/no-ssr.md b/pages/api/no-ssr.md
index d7835a40dd5bd6..6c554d7314144e 100644
--- a/pages/api/no-ssr.md
+++ b/pages/api/no-ssr.md
@@ -22,7 +22,8 @@ This component can be useful in a variety of situations:
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| children * | node | | |
-| fallback | node | <Fallback /> | |
+| defer | bool | false | If `true`, the component will not only prevent server side rendering. It will also defer the rendering of the children into a different screen frame. |
+| fallback | node | null | The fallback content to display. |
Any other properties supplied will be spread to the root element (native element).