Skip to content

Commit

Permalink
[NoSrr] Add a defer property
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Aug 9, 2018
1 parent c937ab0 commit 3a82af3
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 6 deletions.
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

0 comments on commit 3a82af3

Please sign in to comment.