React - componentWillMount()中的setState导致UI延迟
我必须在componentWillMount()
中获取一些数据,这需要一段时间(2秒),在此之后,我使用setState
来更新状态中的某些值,因为这个setState
重新呈现UI,组件初始渲染和setState渲染之间存在延迟,有没有解决此UI延迟的方法?React - componentWillMount()中的setState导致UI延迟
更新: 如果我想使用负载指示,我应该在哪里放呢?我用一个承诺来获取这样我的数据:
componentDidMount() {
api.getData().then((response) => { ... }
在componentWillMount
或constructor
你不应该使用异步操作。
取而代之的是在componentDidMount
。
您可以在DOCS
在此方法将同步设置状态读到它不会引发 重新呈现。避免在此方法中引入任何副作用或订阅 。
编辑
作为随访你更新的问题
,如果我想使用负载指示,我应该在哪里放呢?我使用 承诺获取我的数据
我已经做了一个小例子,提取数据时提取数据和渲染加载器。
在这个例子中,我使用了一个名为jsonplaceholder的免费API测试程序,我用这个URL来获取用户的一些随机数据。
你可以看到,我初始化状态在contructor
与users
空数组,我获取了用户在componentDidMount
生命周期法和更新的承诺的回调内部users
阵列状态已经返回。请注意,我在setTimeOut
方法中做了这个,以便延迟2秒。
现在,阵营不会真的要等待我们的Ajax请求返回的结果,它会不管调用render
方法是什么,因此,在做了render
之前运行的生命周期方法Ajax请求方法(如componentWillMount
或constructor
)不是上述的最佳做法,所以这就是为什么我们在componentDidMount
方法中执行此操作的原因。
您可能会问,好吧然后!但是如何在之前接收数据之前呈现,然后在接收到之后呈现数据?我很高兴你问了:)
我们可以使用反应的生命周期来为我们工作,并利用强大的渲染选项和状态更新。
在本例中,我有条件地在ternary operator的帮助下呈现<Loader />
或数据<UserList/>
。
return ({this.state.users.length > 0 ? <UserList /> : <Loader/>);
这样,当有史以来users
阵列状态的是空它将使Loader
成分,之后的状态将被更新(这将发生在AJAX请求完成之后)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">
\t <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{
\t position:relative;
\t width:125px;
\t height:43px;
\t margin:auto;
}
.escapingBallG{
\t background-color:rgb(0,0,0);
\t position:absolute;
\t top:0;
\t left:0;
\t width:43px;
\t height:43px;
\t border-radius:21px;
\t \t -o-border-radius:21px;
\t \t -ms-border-radius:21px;
\t \t -webkit-border-radius:21px;
\t \t -moz-border-radius:21px;
\t animation-name:bounce_escapingBallG;
\t \t -o-animation-name:bounce_escapingBallG;
\t \t -ms-animation-name:bounce_escapingBallG;
\t \t -webkit-animation-name:bounce_escapingBallG;
\t \t -moz-animation-name:bounce_escapingBallG;
\t animation-duration:1.5s;
\t \t -o-animation-duration:1.5s;
\t \t -ms-animation-duration:1.5s;
\t \t -webkit-animation-duration:1.5s;
\t \t -moz-animation-duration:1.5s;
\t animation-iteration-count:infinite;
\t \t -o-animation-iteration-count:infinite;
\t \t -ms-animation-iteration-count:infinite;
\t \t -webkit-animation-iteration-count:infinite;
\t \t -moz-animation-iteration-count:infinite;
\t animation-timing-function:linear;
\t \t -o-animation-timing-function:linear;
\t \t -ms-animation-timing-function:linear;
\t \t -webkit-animation-timing-function:linear;
\t \t -moz-animation-timing-function:linear;
\t animation-delay:0s;
\t \t -o-animation-delay:0s;
\t \t -ms-animation-delay:0s;
\t \t -webkit-animation-delay:0s;
\t \t -moz-animation-delay:0s;
\t transform:scale(0.5, 1);
\t \t -o-transform:scale(0.5, 1);
\t \t -ms-transform:scale(0.5, 1);
\t \t -webkit-transform:scale(0.5, 1);
\t \t -moz-transform:scale(0.5, 1);
}
@keyframes bounce_escapingBallG{
\t 0%{
\t \t left:0px;
\t \t transform:scale(0.5, 1);
\t }
\t 25%{
\t \t left:41px;
\t \t transform:scale(1, 0.5);
\t }
\t 50%{
\t \t left:103px;
\t \t transform:scale(0.5, 1);
\t }
\t 75%{
\t \t left:41px;
\t \t transform:scale(1, 0.5);
\t }
\t 100%{
\t \t left:0px;
\t \t transform:scale(0.5, 1);
\t }
}
@-o-keyframes bounce_escapingBallG{
\t 0%{
\t \t left:0px;
\t \t -o-transform:scale(0.5, 1);
\t }
\t 25%{
\t \t left:41px;
\t \t -o-transform:scale(1, 0.5);
\t }
\t 50%{
\t \t left:103px;
\t \t -o-transform:scale(0.5, 1);
\t }
\t 75%{
\t \t left:41px;
\t \t -o-transform:scale(1, 0.5);
\t }
\t 100%{
\t \t left:0px;
\t \t -o-transform:scale(0.5, 1);
\t }
}
@-ms-keyframes bounce_escapingBallG{
\t 0%{
\t \t left:0px;
\t \t -ms-transform:scale(0.5, 1);
\t }
\t 25%{
\t \t left:41px;
\t \t -ms-transform:scale(1, 0.5);
\t }
\t 50%{
\t \t left:103px;
\t \t -ms-transform:scale(0.5, 1);
\t }
\t 75%{
\t \t left:41px;
\t \t -ms-transform:scale(1, 0.5);
\t }
\t 100%{
\t \t left:0px;
\t \t -ms-transform:scale(0.5, 1);
\t }
}
@-webkit-keyframes bounce_escapingBallG{
\t 0%{
\t \t left:0px;
\t \t -webkit-transform:scale(0.5, 1);
\t }
\t 25%{
\t \t left:41px;
\t \t -webkit-transform:scale(1, 0.5);
\t }
\t 50%{
\t \t left:103px;
\t \t -webkit-transform:scale(0.5, 1);
\t }
\t 75%{
\t \t left:41px;
\t \t -webkit-transform:scale(1, 0.5);
\t }
\t 100%{
\t \t left:0px;
\t \t -webkit-transform:scale(0.5, 1);
\t }
}
@-moz-keyframes bounce_escapingBallG{
\t 0%{
\t \t left:0px;
\t \t -moz-transform:scale(0.5, 1);
\t }
\t 25%{
\t \t left:41px;
\t \t -moz-transform:scale(1, 0.5);
\t }
\t 50%{
\t \t left:103px;
\t \t -moz-transform:scale(0.5, 1);
\t }
\t 75%{
\t \t left:41px;
\t \t -moz-transform:scale(1, 0.5);
\t }
\t 100%{
\t \t left:0px;
\t \t -moz-transform:scale(0.5, 1);
\t }
}
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
根据你与你取得的数据做什么(例如渲染项目的一长串),你可能考虑以较小的块或页面获取数据。这将允许您的组件渲染一小部分数据,并且您可以在幕后或用户与视图进行交互时加载其余部分。
不知道这是否有帮助,您可以添加加载状态,直到获取所有数据和setState。然后使装载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>
);
}
}
为什么?哦为什么?当文档明确表示要避免这种模式时,您会推荐这种方法吗? –
@ sag1v你是在说加载状态?你能指点我的文件,以避免加载状态,我真的很好奇。 – pk1m
@ pk1m我在讨论'componentWillMount'中的副作用。你可以在我的回答中看到它的链接。 –
如果花费2秒至获取数据,总是会有初始渲染和'setState'触发呈现介于2秒的延迟。你是否在提取数据之前询问如何防止所有渲染? –
不,可以防止所有渲染吗? – Adel
我的意思不是SPA应用程序的全部要点?不是使用数据从服务器下载HTML,而是首先加载框架并在异步返回后加载数据。我不知道你将如何避免这种情况,它要么加载骨架(有加载指标),然后加载数据或保持页面空白,并加载数据+骨架后,我不知道为什么这会更好。对我来说 – pk1m