The goal
When the html page is rendered, display a spinner immediately (while React loads), and hide it after React is ready.
Since the spinner is rendered in pure HTML/CSS (outside of the React domain), React shouldn't control the showing/hiding process directly, and the implementation should be transparent to React.
Solution 1 - the :empty pseudo-class
Since you render react into a DOM container - <div id="app"></div>
, you can add a spinner to that container, and when react will load and render, the spinner will disappear.
You can't add a DOM element (a div for example) inside the react root, since React will replace the contents of the container as soon as ReactDOM.render()
is called. Even if you render null
, the content would still be replaced by a comment - <!-- react-empty: 1 -->
. This means that if you want to display the loader while the main component mounts, data is loading, but nothing is actually rendered, a loader markup placed inside the container (<div id="app"><div class="loader"></div></div>
for example) would not work.
A workaround is to add the spinner class to the react container, and use the :empty
pseudo class. The spinner will be visible, as long as nothing is rendered into the container (comments don't count). As soon as react renders something other than comment, the loader will disappear.
Example 1
In the example you can see a component that renders null
until it's ready. The container is the loader as well - <div id="app" class="app"></div>
, and the loader's class will only work if it's :empty
(see comments in code):
class App extends React.Component {
state = {
loading: true
};
componentDidMount() {
// this simulates an async action, after which the component will render the content
demoAsyncCall().then(() => this.setState({ loading: false }));
}
render() {
const { loading } = this.state;
if(loading) { // if your component doesn't have to wait for an async action, remove this block
return null; // render null when app is not ready
}
return (
<div>I'm the app</div>
);
}
}
function demoAsyncCall() {
return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
.loader:empty {
position: absolute;
top: calc(50% - 4em);
left: calc(50% - 4em);
width: 6em;
height: 6em;
border: 1.1em solid rgba(0, 0, 0, 0.2);
border-left: 1.1em solid #000000;
border-radius: 50%;
animation: load8 1.1s infinite linear;
}
@keyframes load8 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.1/react-dom.js"></script>
<div id="app" class="loader"></div> <!-- add class loader to container -->
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…