这是一个来自谷歌Adsense应用页面的例子。加载界面显示在主界面之前。
我不知道如何用React做同样的事情,因为如果我用React组件渲染加载屏幕,它不会在页面加载时显示,因为它必须等待DOM渲染之前。
更新:
我通过将屏幕加载器放在index.html中并在React componentDidMount()生命周期方法中删除它来举例说明我的方法。
示例和反应加载屏幕。
这是一个来自谷歌Adsense应用页面的例子。加载界面显示在主界面之前。
我不知道如何用React做同样的事情,因为如果我用React组件渲染加载屏幕,它不会在页面加载时显示,因为它必须等待DOM渲染之前。
更新:
我通过将屏幕加载器放在index.html中并在React componentDidMount()生命周期方法中删除它来举例说明我的方法。
示例和反应加载屏幕。
当前回答
你可以通过在react中使用惰性加载轻松做到这一点。 为此你必须使用惰性和悬疑反应。
import React, { lazy, Suspense } from 'react';
const loadable = (importFunc, { fallback = null } = { fallback: null }) => {
const LazyComponent = lazy(importFunc);
return props => (
<Suspense fallback={fallback}>
<LazyComponent {...props} />
</Suspense>
);
};
export default loadable;
然后像这样导出你的组件。
export const TeacherTable = loadable(() =>
import ('./MainTables/TeacherTable'), {
fallback: <Loading />,
});
然后在你的路由文件中像这样使用它。
<Route exact path="/app/view/teachers" component={TeacherTable} />
这就是现在你很好去每次你的DOM渲染你的加载组件将显示为我们已经在上面的fallback属性中指定。只要确保你只在componentDidMount()中做任何ajax请求
其他回答
来自React文档,源代码。
React.lazy function lets you render a dynamic import as a regular component. This will automatically load the bundle containing the OtherComponent when this component is first rendered. React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.
import React, { Suspense } from 'react';
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<OtherComponent />
</Suspense>
</div>
);
}
回退道具接受你想渲染的任何React元素 在等待组件加载时。你可以设置悬念 组件位于惰性组件之上。你甚至可以包裹 多个惰性组件和一个悬疑组件。
源
我不知道现在回答是否太晚,因为你可能已经找到了解决方案,但这里有一个来自我的观点,因为这个问题真的很有用。: 我在scrimba.com上上了一堂课,在这里,老师从课堂开始,然后开始讲课。他通过课堂和状态来教授API调用。这是他的代码:
import React, {Component} from "react"
class App extends Component {
constructor() {
super()
this.state = {
loading: false,
character: {}
}
}
componentDidMount() {
this.setState({loading: true})
fetch("https://swapi.dev/api/people/1/")
.then(response => response.json())
.then(data => {
this.setState({
loading: false,
character: data
})
})
}
render() {
const text = this.state.loading ? "loading..." : this.state.character.name
return (
<div>
<p>{text}</p>
</div>
)
}
}
export default App
这很直接,在开始时将加载状态设置为true并保持它,直到接收到数据,然后当接收到数据时,更改状态并将加载设置为false并显示内容。 现在我试着用钩子,作为练习,它工作得很顺利!一个简单而有效的解决方案。这是我的代码:
import React, {useState,useEffect} from 'react'
function App()
{
const [response, setResponse] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchResponse() ;
} , []);
const fetchResponse = async () => {
const data = await fetch("https://swapi.dev/api/people/1/");
const response = await data.json();
setResponse(response);
console.log(response.name);
setLoading(false);
}
const content = loading ? <i className="fas fa-atom fa-spin"></i> : <h1>{response.name}</h1>
return(
<section id="w-d-p">
{content}
</section>
)
}
export default App;
钩子也是一样的逻辑。在数据加载时,我得到了漂亮的转轮然后,我的数据!
哦,顺便说一下,如果你不喜欢这个XD,你可以在fetch中放入自己的API。
我们的目标
当html页面被渲染时,立即显示一个转轮(在React加载时),并在React准备好后隐藏它。
由于旋转器是在纯HTML/CSS中呈现的(在React域之外),React不应该直接控制显示/隐藏过程,实现对React应该是透明的。
解决方案1 -:empty伪类
因为你把react渲染到一个DOM容器中- <div id="app"></div>,你可以在这个容器中添加一个旋转器,当react加载并渲染时,旋转器将消失。
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.
一个解决方法是将spinner类添加到react容器中,并使用:empty伪类。旋转器将是可见的,只要没有任何东西被呈现到容器中(注释不算数)。一旦react渲染了注释以外的东西,加载器就会消失。
示例1
在这个例子中,你可以看到一个组件在准备好之前呈现为空。容器也是加载器- <div id="app" class="app"></div>,并且加载器的类只有在它为:空时才会工作(见代码中的注释):
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伪类来显示/隐藏选择器的一种变体是将spinner设置为应用容器的兄弟元素,并且只要容器为空,就使用相邻的兄弟组合子(+)显示它:
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,并通过props将它们传递给根容器。这些函数可以操作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>
现在我们也可以在React 16.8中使用钩子:
import React, { useState, useEffect } from 'react';
const App = () => {
const [ spinner, setSpinner ] = useState(true);
// It will be executed before rendering
useEffect(() => {
setTimeout(() => setSpinner(false), 1000)
}, []);
// [] means like componentDidMount
return !spinner && <div>Your content</div>;
};
export default App;
解决这个问题的方法是在你的渲染函数中这样做:
constructor() {
this.state = { isLoading: true }
}
componentDidMount() {
this.setState({isLoading: false})
}
render() {
return(
this.state.isLoading ? *showLoadingScreen* : *yourPage()*
)
}
在构造函数中将isLoading初始化为true,在componentDidMount上将其初始化为false。