我必须在componentWillMount()
中获取一些数据,这需要一些时间(2秒),之后,我使用setState
来更新状态中的某些值,因为setState
重新呈现UI,组件初始渲染和setState渲染之间存在延迟,有没有办法解决此UI滞后问题?
更新: 如果我想使用加载指示器,我应该把它放在哪里?我使用承诺来获取这样的数据:
componentDidMount() {
api.getData().then((response) => { ... }
答案 0 :(得分:10)
您绝不应在componentWillMount
或constructor
中使用异步操作
而是在componentDidMount
中进行
您可以在DOCS
在此方法中同步设置状态不会触发a 重新渲染。避免引入任何副作用或订阅 这种方法。
修改强>
作为更新后问题的后续内容
如果我想使用加载指示器,我应该把它放在哪里?我用了一个 承诺获取我的数据
我做了一个小例子,在获取数据的同时获取数据和渲染加载器。
在此示例中,我使用名为jsonplaceholder的免费API测试程序,我正在使用此URL来获取用户的一些随机数据。
您可以看到我使用contructor
的空数组初始化users
中的状态,我在componentDidMount
生命中获取用户循环方法并更新已返回的promise的回调内的状态的users
数组。请注意,我在setTimeOut
方法中执行此操作以获得2秒的延迟。
现在,React不会真的等待我们的ajax请求返回结果,无论如何都会调用render
方法,因此在运行的生命周期方法中执行ajax请求在render
方法之前(如componentWillMount
或constructor
)不是上面提到的最佳做法,这就是我们在componentDidMount
方法中执行此操作的原因。
你可能会问,好吧!但是,我应该如何在收到数据之前呈现 ,然后在之后呈现数据?我很高兴你问:)
我们可以使用反应的生命周期为我们工作,并利用强大的渲染选项和状态更新
在此示例中,我在ternary operator的帮助下有条件地呈现了<Loader />
或数据<UserList/>
。
return ({this.state.users.length > 0 ? <UserList /> : <Loader/>);
这样,当users
状态数组为空时,它将呈现Loader
组件,在状态更新后(这将在ajax请求完成后发生){{1 }}方法将再次被调用,但这次条件将返回render
,因此true
将呈现,不 UserList
。
以下是完整的示例:
Loader
const apiUrl = "https://jsonplaceholder.typicode.com/users";
const User = ({ name, username, email }) => (
<div style={{ border: "1px solid #ccc", padding: "15px" }}>
<div>Name: {name}</div>
<div>User Name: {username}</div>
<div>E-Mail: {email}</div>
</div>
);
const UserList = ({ users }) =>(
<div>
{users.map(user => <User key={user.id} {...user} />)}
</div>
);
const Loader = () => (
<div id="escapingBallG">
<div id="escapingBall_1" className="escapingBallG"></div>
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
users: []
};
}
componentDidMount() {
// mimic 2 seconds delay
setTimeout(() => {
axios.get(apiUrl)
.then(users => {
this.setState({
users: [...users.data]
});
})
.catch(err => console.log(err));
}, 2000);
}
render() {
const { users } = this.state;
return (
<div>
{
users.length > 0 ? <UserList users={users} /> : <Loader />
}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
#escapingBallG{
position:relative;
width:125px;
height:43px;
margin:auto;
}
.escapingBallG{
background-color:rgb(0,0,0);
position:absolute;
top:0;
left:0;
width:43px;
height:43px;
border-radius:21px;
-o-border-radius:21px;
-ms-border-radius:21px;
-webkit-border-radius:21px;
-moz-border-radius:21px;
animation-name:bounce_escapingBallG;
-o-animation-name:bounce_escapingBallG;
-ms-animation-name:bounce_escapingBallG;
-webkit-animation-name:bounce_escapingBallG;
-moz-animation-name:bounce_escapingBallG;
animation-duration:1.5s;
-o-animation-duration:1.5s;
-ms-animation-duration:1.5s;
-webkit-animation-duration:1.5s;
-moz-animation-duration:1.5s;
animation-iteration-count:infinite;
-o-animation-iteration-count:infinite;
-ms-animation-iteration-count:infinite;
-webkit-animation-iteration-count:infinite;
-moz-animation-iteration-count:infinite;
animation-timing-function:linear;
-o-animation-timing-function:linear;
-ms-animation-timing-function:linear;
-webkit-animation-timing-function:linear;
-moz-animation-timing-function:linear;
animation-delay:0s;
-o-animation-delay:0s;
-ms-animation-delay:0s;
-webkit-animation-delay:0s;
-moz-animation-delay:0s;
transform:scale(0.5, 1);
-o-transform:scale(0.5, 1);
-ms-transform:scale(0.5, 1);
-webkit-transform:scale(0.5, 1);
-moz-transform:scale(0.5, 1);
}
@keyframes bounce_escapingBallG{
0%{
left:0px;
transform:scale(0.5, 1);
}
25%{
left:41px;
transform:scale(1, 0.5);
}
50%{
left:103px;
transform:scale(0.5, 1);
}
75%{
left:41px;
transform:scale(1, 0.5);
}
100%{
left:0px;
transform:scale(0.5, 1);
}
}
@-o-keyframes bounce_escapingBallG{
0%{
left:0px;
-o-transform:scale(0.5, 1);
}
25%{
left:41px;
-o-transform:scale(1, 0.5);
}
50%{
left:103px;
-o-transform:scale(0.5, 1);
}
75%{
left:41px;
-o-transform:scale(1, 0.5);
}
100%{
left:0px;
-o-transform:scale(0.5, 1);
}
}
@-ms-keyframes bounce_escapingBallG{
0%{
left:0px;
-ms-transform:scale(0.5, 1);
}
25%{
left:41px;
-ms-transform:scale(1, 0.5);
}
50%{
left:103px;
-ms-transform:scale(0.5, 1);
}
75%{
left:41px;
-ms-transform:scale(1, 0.5);
}
100%{
left:0px;
-ms-transform:scale(0.5, 1);
}
}
@-webkit-keyframes bounce_escapingBallG{
0%{
left:0px;
-webkit-transform:scale(0.5, 1);
}
25%{
left:41px;
-webkit-transform:scale(1, 0.5);
}
50%{
left:103px;
-webkit-transform:scale(0.5, 1);
}
75%{
left:41px;
-webkit-transform:scale(1, 0.5);
}
100%{
left:0px;
-webkit-transform:scale(0.5, 1);
}
}
@-moz-keyframes bounce_escapingBallG{
0%{
left:0px;
-moz-transform:scale(0.5, 1);
}
25%{
left:41px;
-moz-transform:scale(1, 0.5);
}
50%{
left:103px;
-moz-transform:scale(0.5, 1);
}
75%{
left:41px;
-moz-transform:scale(1, 0.5);
}
100%{
left:0px;
-moz-transform:scale(0.5, 1);
}
}
答案 1 :(得分:2)
不知道这是否有帮助,您可以添加加载状态,直到获得所有数据和setState。然后将loading sate设为false,然后渲染数据
class Project extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data : data
};
}
componentDidMount() {
this
.props
.GetData(this.state)
.then((res) => {
this.setState.isLoading = false;
}, (err) => this.setState({errors: err.response}));
}
render() {
const { isLoading, data} = this.props;
if(isLoading){
return (
<p>loading!!!</p>
);
return (
<p>data</p>
);
}
}
答案 2 :(得分:1)
根据您对提取的数据执行的操作(例如,呈现一长串项目),您可以考虑以较小的块或页面提取数据。这将允许您的组件呈现一小组数据,您可以在幕后加载其余内容,或者在用户与视图交互时加载。