Mục đích
Khi trang html được hiển thị, hiển thị một công cụ quay vòng ngay lập tức (trong khi React tải) và ẩn nó sau khi React sẵn sàng.
Vì spinner được hiển thị bằng HTML / CSS thuần (bên ngoài miền React), React không nên trực tiếp kiểm soát quá trình hiển thị / ẩn và việc triển khai phải trong suốt đối với React.
Giải pháp 1 - lớp: giả giả
Vì bạn kết xuất phản ứng vào một thùng chứa DOM - <div id="app"></div>
, bạn có thể thêm một công cụ quay vòng vào bộ chứa đó và khi phản ứng sẽ tải và kết xuất, công cụ quay vòng sẽ biến mất.
Bạn không thể thêm một phần tử DOM (ví dụ div) bên trong gốc phản ứng, vì React sẽ thay thế nội dung của vùng chứa ngay khi ReactDOM.render()
được gọi. Ngay cả khi bạn kết xuất null
, nội dung vẫn sẽ được thay thế bằng một nhận xét - <!-- react-empty: 1 -->
. Điều này có nghĩa là nếu bạn muốn hiển thị trình tải trong khi các thành phần chính gắn kết, dữ liệu đang tải, nhưng không có gì thực sự được hiển thị, một đánh dấu trình tải được đặt bên trong container (<div id="app"><div class="loader"></div></div>
ví dụ) sẽ không hoạt động.
Một cách giải quyết là thêm lớp spinner vào thùng chứa phản ứng và sử dụng :empty
lớp giả . Công cụ quay vòng sẽ hiển thị, miễn là không có gì được hiển thị trong vùng chứa (ý kiến không được tính). Ngay khi phản ứng ám chỉ thứ gì đó ngoài bình luận, bộ nạp sẽ biến mất.
ví dụ 1
Trong ví dụ này, bạn có thể thấy một thành phần hiển thị null
cho đến khi nó sẵn sàng. Container cũng là trình nạp - <div id="app" class="app"></div>
và lớp của trình tải sẽ chỉ hoạt động nếu nó :empty
(xem các bình luận trong mã):
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 -->
Ví dụ 2
Một biến thể của việc sử dụng :empty
lớp giả để hiển thị / ẩn bộ chọn, đang đặt spinner làm thành phần anh chị em với vùng chứa ứng dụng và hiển thị nó miễn là vùng chứa trống bằng cách sử dụng bộ kết hợp anh chị em liền kề ( +
):
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>
Giải pháp 2 - Truyền spinner "xử lý" làm đạo cụ
Để có một kiểm soát tốt hạt hơn so với trạng thái spinners hiển thị, tạo ra hai chức năng showSpinner
và hideSpinner
, và vượt qua chúng để container gốc qua đạo cụ. Các chức năng có thể thao tác DOM hoặc làm bất cứ điều gì cần thiết để kiểm soát spinner. Theo cách này, React không nhận thức được "thế giới bên ngoài", cũng không cần phải kiểm soát DOM trực tiếp. Bạn có thể dễ dàng thay thế các chức năng để kiểm tra hoặc nếu bạn cần thay đổi logic và bạn có thể chuyển chúng cho các thành phần khác trong cây React.
ví dụ 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>
Ví dụ 2 - móc
Ví dụ này sử dụng useEffect
hook để ẩn spinner sau khi mount thành phần.
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>