Skip to content

Instantly share code, notes, and snippets.

@zackargyle
Last active March 28, 2017 18:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zackargyle/0d3fc574b45cf756ebac44c7ba36b75d to your computer and use it in GitHub Desktop.
Save zackargyle/0d3fc574b45cf756ebac44c7ba36b75d to your computer and use it in GitHub Desktop.
React component for deferring rendering until post-mount.
/*
<DeferredMount ssr />
This component defers rendering of its children until after mounting, good
for below-the-fold content. If the content should be rendered syncronously
on the server you can set the `ssr` prop. If `ssr` is set to true, there
will be a client/server mismatch unless you can somehow determine that we
are hydrating from the server.
Is there a better way than the hack below?
*/
// flow
import { Component } from 'react';
type Props = {
children: any,
ssr?: true,
timeout?: number,
};
type State = {
mounted: boolean,
};
const IS_SERVER = typeof window === 'undefined';
/* This is hacky. If it has been server rendered, we
* need a way to know. This `isHydrating` flag tells us
* that we're probably in the server checksum stage.
* So render sync if props.ssr was set.
*/
let isHydrating = true;
if (!IS_SERVER) {
if (document.readyState === 'complete') {
isHydrating = false;
} else {
window.addEventListener('load', () => {
isHydrating = false;
});
}
}
const shouldRender = (props, state) => (
(state.mounted) ||
(props.ssr && (IS_SERVER || isHydrating))
);
export default class DeferredMount extends Component {
state: State = {
mounted: false,
};
componentDidMount() {
this.mounted = true;
setTimeout(() => {
if (this.mounted) {
this.setState({ mounted: true });
}
}, this.props.timeout || 0);
}
componentWillUnmount() {
this.mounted = false;
}
props: Props;
render() {
const { props, state } = this;
return shouldRender(props, state) ? props.children : null;
}
}
@zackargyle
Copy link
Author

You use it like this

<DeferredMount ssr>
    <div>Hello world</div>
</DeferredMount>

Problem is I don't see a good way to defer mounting if it's rendered client side, but render synchronously if it's rendered server side, without having a client/server mismatch. The above code works, but the window.readyState/onload hack is pretty gross. Ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment