Цель
Когда страница html отображается, немедленно отобразите счетчик (пока React загружается) и скройте его после того, как React будет готов.
Поскольку счетчик отображается в чистом HTML / CSS (вне домена React), React не должен напрямую управлять процессом отображения / скрытия, а реализация должна быть прозрачной для React.
Решение 1.Пустой псевдокласс
Поскольку вы визуализируете реакцию в контейнер DOM - <div id="app"></div>, вы можете добавить счетчик в этот контейнер, и когда реакция загрузится и отобразится, счетчик исчезнет.
Вы не можете добавить элемент DOM (например, div) в корень реакции, поскольку React заменит содержимое контейнера, как только он ReactDOM.render()будет вызван. Даже если вы nullвыполните рендеринг , контент все равно будет заменен комментарием - <!-- react-empty: 1 -->. Это означает, что если вы хотите отобразить загрузчик во время монтирования основного компонента, данные загружаются, но на самом деле ничего не отображается, разметка загрузчика, размещенная внутри контейнера ( <div id="app"><div class="loader"></div></div>например), не будет работать.
Обходной путь - добавить класс счетчика в контейнер реакции и использовать :emptyпсевдокласс . Счетчик будет виден, пока в контейнер ничего не отображается (комментарии не учитываются). Как только response отобразит что-то кроме комментария, загрузчик исчезнет.
Пример 1
В этом примере вы можете увидеть компонент, который рендерится, nullпока не будет готов. Контейнер также является загрузчиком <div id="app" class="app"></div>, а класс загрузчика будет работать, только если он :empty(см. Комментарии в коде):
class App extends React.Component {
state = {
loading: true
};
componentDidMount() {
demoAsyncCall().then(() => this.setState({ loading: false }));
}
render() {
const { loading } = this.state;
if(loading) {
return null;
}
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>
Пример 2
Разновидностью использования :emptyпсевдокласса для отображения / скрытия селектора является установка счетчика как родственного элемента для контейнера приложения и его отображение до тех пор, пока контейнер пуст, с помощью соседнего родственного комбинатора ( +):
class App extends React.Component {
state = {
loading: true
};
componentDidMount() {
demoAsyncCall().then(() => this.setState({ loading: false }));
}
render() {
const { loading } = this.state;
if(loading) {
return null;
}
return (
<div>I'm the app</div>
);
}
}
function demoAsyncCall() {
return new Promise((resolve) => setTimeout(() => resolve(), 2500));
}
ReactDOM.render(
<App />,
document.getElementById('app')
);
#app:not(:empty) + .sk-cube-grid {
display: none;
}
.sk-cube-grid {
width: 40px;
height: 40px;
margin: 100px auto;
}
.sk-cube-grid .sk-cube {
width: 33%;
height: 33%;
background-color: #333;
float: left;
animation: sk-cubeGridScaleDelay 1.3s infinite ease-in-out;
}
.sk-cube-grid .sk-cube1 {
animation-delay: 0.2s;
}
.sk-cube-grid .sk-cube2 {
animation-delay: 0.3s;
}
.sk-cube-grid .sk-cube3 {
animation-delay: 0.4s;
}
.sk-cube-grid .sk-cube4 {
animation-delay: 0.1s;
}
.sk-cube-grid .sk-cube5 {
animation-delay: 0.2s;
}
.sk-cube-grid .sk-cube6 {
animation-delay: 0.3s;
}
.sk-cube-grid .sk-cube7 {
animation-delay: 0s;
}
.sk-cube-grid .sk-cube8 {
animation-delay: 0.1s;
}
.sk-cube-grid .sk-cube9 {
animation-delay: 0.2s;
}
@keyframes sk-cubeGridScaleDelay {
0%,
70%,
100% {
transform: scale3D(1, 1, 1);
}
35% {
transform: scale3D(0, 0, 1);
}
}
<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"></div>
<div class="sk-cube-grid">
<div class="sk-cube sk-cube1"></div>
<div class="sk-cube sk-cube2"></div>
<div class="sk-cube sk-cube3"></div>
<div class="sk-cube sk-cube4"></div>
<div class="sk-cube sk-cube5"></div>
<div class="sk-cube sk-cube6"></div>
<div class="sk-cube sk-cube7"></div>
<div class="sk-cube sk-cube8"></div>
<div class="sk-cube sk-cube9"></div>
</div>
Решение 2. Передайте «обработчики» счетчиков в качестве реквизита.
Чтобы иметь более детальный контроль над состоянием отображения счетчиков, создайте две функции showSpinnerи hideSpinnerи передайте их корневому контейнеру через props. Функции могут управлять DOM или делать все необходимое для управления счетчиком. Таким образом, React не знает о «внешнем мире» и ему не нужно напрямую контролировать DOM. Вы можете легко заменить функции для тестирования или, если вам нужно изменить логику, и передать их другим компонентам в дереве React.
Пример 1
const loader = document.querySelector('.loader');
const showLoader = () => loader.classList.remove('loader--hide');
const hideLoader = () => loader.classList.add('loader--hide');
class App extends React.Component {
componentDidMount() {
this.props.hideLoader();
}
render() {
return (
<div>I'm the app</div>
);
}
}
setTimeout(() =>
ReactDOM.render(
<App
hideLoader={hideLoader}
showLoader={showLoader}
/>,
document.getElementById('app')
)
, 1000);
.loader {
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;
transition: opacity 0.3s;
}
.loader--hide {
opacity: 0;
}
@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"></div>
<div class="loader"></div>
Пример 2 - крючки
В этом примере useEffectкрючок используется для скрытия счетчика после монтирования компонента.
const { useEffect } = React;
const loader = document.querySelector('.loader');
const showLoader = () => loader.classList.remove('loader--hide');
const hideLoader = () => loader.classList.add('loader--hide');
const App = ({ hideLoader }) => {
useEffect(hideLoader, []);
return (
<div>I'm the app</div>
);
}
setTimeout(() =>
ReactDOM.render(
<App
hideLoader={hideLoader}
showLoader={showLoader}
/>,
document.getElementById('app')
)
, 1000);
.loader {
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;
transition: opacity 0.3s;
}
.loader--hide {
opacity: 0;
}
@keyframes load8 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="app"></div>
<div class="loader"></div>