목표
html 페이지가 렌더링되면 스피너를 즉시 표시하고 (React가로드되는 동안) React가 준비되면 숨 깁니다.
스피너는 순수한 HTML / CSS (React 도메인 외부)로 렌더링되므로 React는 표시 / 숨김 프로세스를 직접 제어하지 않아야하며 구현은 React에 투명해야합니다.
해결 방법 1-: 비상 의사 클래스
반응을 DOM 컨테이너에 렌더링하기 때문에 <div id="app"></div>
해당 컨테이너에 스피너를 추가 할 수 있으며 반응이로드되어 렌더링되면 스피너가 사라집니다.
React ReactDOM.render()
가 호출 되는 즉시 컨테이너의 내용을 대체하므로 반응 루트 내에 DOM 요소 (예 : div)를 추가 할 수 없습니다 . 렌더링하더라도 null
내용은 여전히 주석으로 대체됩니다 <!-- react-empty: 1 -->
. 즉, 주요 구성 요소가 마운트되는 동안 로더를 표시하려는 경우 데이터가로드되지만 실제로 아무것도 렌더링 <div id="app"><div class="loader"></div></div>
되지 않으면 컨테이너 ( 예 :) 내부에 배치 된 로더 마크 업 이 작동하지 않습니다.
해결 방법은 스피너 클래스를 반응 컨테이너에 추가하고 :empty
의사 클래스를 사용하는 것 입니다. 컨테이너에 아무것도 렌더링되지 않는 한 스피너가 표시됩니다 (코멘트는 포함되지 않음). 반응이 주석 이외의 것을 렌더링하자마자 로더가 사라집니다.
실시 예 1
이 예에서는 null
준비 될 때까지 렌더링되는 구성 요소를 볼 수 있습니다 . 컨테이너도 로더입니다- <div id="app" class="app"></div>
그리고 로더의 클래스는 그것이 작동하는 경우에만 작동합니다 :empty
(코드 주석 참조).
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 -->
실시 예 2
:empty
의사 클래스를 사용하여 선택기를 표시하거나 숨기는 변형 은 스피너를 앱 컨테이너의 형제 요소로 설정하고 인접한 형제 조합기 ( +
)를 사용하여 컨테이너가 비어있는 동안 표시하는 것입니다 .
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 async data, 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')
);
#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>
<!-- add class loader to container -->
<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
, 소품을 통해 루트 컨테이너로 전달합니다. 함수는 DOM을 조작하거나 스피너를 제어하는 데 필요한 모든 작업을 수행 할 수 있습니다. 이런 식으로 React는 "외부 세계"를 인식하지 못하고 DOM을 직접 제어 할 필요가 없습니다. 테스트 할 기능을 쉽게 교체하거나 로직을 변경해야하는 경우 React 트리의 다른 구성 요소로 전달할 수 있습니다.
실시 예 1
const loader = document.querySelector('.loader');
// if you want to show the loader when React loads data again
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>
);
}
}
// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() =>
// the show/hide functions are passed as props
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');
// if you want to show the loader when React loads data again
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>
);
}
// the setTimeout simulates the time it takes react to load, and is not part of the solution
setTimeout(() =>
// the show/hide functions are passed as props
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>