Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NoSrr] Add a defer property #12462

Merged
merged 1 commit into from
Aug 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/src/pages/utils/no-ssr/no-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,16 @@ This component can be useful in a variety of situations:
- Improve the time-to-first paint on the client by only rendering above the fold.
- Reduce the rendering time on the server.
- Under too heavy server load, you can turn on service degradation.
- Improve the time-to-interactive by only rendering what's important (with the `defer` property).

## Client side deferring

{{"demo": "pages/utils/no-ssr/SimpleNoSsr.js"}}

## Frame deferring

In it's core, the NoSsr component purpose is to **defer rendering**.
As it's illustrated in the previous demo, you can use it to defer the rendering from the server to the client.

But you can also use it to defer the rendering within the client itself.
You can **wait a screen frame** with the `defer` property to render the children.
35 changes: 31 additions & 4 deletions packages/material-ui/src/NoSsr/NoSsr.js
Original file line number Diff line number Diff line change
Expand Up @@ -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).
*
Expand All @@ -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() {
Expand All @@ -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: <Fallback />,
defer: false,
fallback: null,
};

export default NoSsr;
20 changes: 19 additions & 1 deletion packages/material-ui/src/NoSsr/NoSsr.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ describe('<NoSsr />', () => {
<span>Hello</span>
</NoSsr>,
);
assert.strictEqual(wrapper.name(), 'Fallback');
assert.strictEqual(wrapper.name(), null);
});
});

Expand All @@ -37,6 +37,7 @@ describe('<NoSsr />', () => {
</NoSsr>,
);
assert.strictEqual(wrapper.find('span').length, 1);
assert.strictEqual(wrapper.text(), 'Hello');
});
});

Expand All @@ -50,4 +51,21 @@ describe('<NoSsr />', () => {
assert.strictEqual(wrapper.text(), 'fallback');
});
});

describe('prop: defer', () => {
it('should defer the rendering', done => {
const wrapper = mount(
<NoSsr defer>
<span>Hello</span>
</NoSsr>,
);
assert.strictEqual(wrapper.find('span').length, 0);
setTimeout(() => {
wrapper.update();
assert.strictEqual(wrapper.find('span').length, 1);
assert.strictEqual(wrapper.text(), 'Hello');
done();
}, 100);
});
});
});
3 changes: 2 additions & 1 deletion pages/api/no-ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ This component can be useful in a variety of situations:
| Name | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| <span class="prop-name required">children *</span> | <span class="prop-type">node |   | |
| <span class="prop-name">fallback</span> | <span class="prop-type">node | <span class="prop-default">&lt;Fallback /></span> | |
| <span class="prop-name">defer</span> | <span class="prop-type">bool | <span class="prop-default">false</span> | 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. |
| <span class="prop-name">fallback</span> | <span class="prop-type">node | <span class="prop-default">null</span> | The fallback content to display. |

Any other properties supplied will be spread to the root element (native element).

Expand Down