React - componentWillMount()中的setState导致UI延迟

问题描述:

我必须在componentWillMount()中获取一些数据,这需要一段时间(2秒),在此之后,我使用setState来更新状态中的某些值,因为这个setState重新呈现UI,组件初始渲染和setState渲染之间存在延迟,有没有解决此UI延迟的方法?React - componentWillMount()中的setState导致UI延迟

更新: 如果我想使用负载指示,我应该在哪里放呢?我用一个承诺来获取这样我的数据:

componentDidMount() { 
    api.getData().then((response) => { ... } 
+1

如果花费2秒至获取数据,总是会有初始渲染和'setState'触发呈现介于2秒的延迟。你是否在提取数据之前询问如何防止所有渲染? –

+0

不,可以防止所有渲染吗? – Adel

+0

我的意思不是SPA应用程序的全部要点?不是使用数据从服务器下载HTML,而是首先加载框架并在异步返回后加载数据。我不知道你将如何避免这种情况,它要么加载骨架(有加载指标),然后加载数据或保持页面空白,并加载数据+骨架后,我不知道为什么这会更好。对我来说 – pk1m

componentWillMountconstructor你不应该使用异步操作。
取而代之的是在componentDidMount
您可以在DOCS

在此方法将同步设置状态读到它不会引发 重新呈现。避免在此方法中引入任何副作用或订阅 。

编辑
作为随访你更新的问题

,如果我想使用负载指示,我应该在哪里放呢?我使用 承诺获取我的数据

我已经做了一个小例子,提取数据时提取数据和渲染加载器。

在这个例子中,我使用了一个名为jsonplaceholder的免费API测试程序,我用这个URL来获取用户的一些随机数据。
你可以看到,我初始化状态contructorusers空数组,我获取了用户componentDidMount生命周期法和更新的承诺的回调内部users阵列状态已经返回。请注意,我在setTimeOut方法中做了这个,以便延迟2秒。

现在,阵营不会真的要等待我们的Ajax请求返回的结果,它会不管调用render方法是什么,因此,在做了render之前运行的生命周期方法Ajax请求方法(如componentWillMountconstructor)不是上述的最佳做法,所以这就是为什么我们在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>

+0

,componentDidMount导致同样的问题。 – Adel

+0

怎么这么? componentDidMount将在'render'之后被调用,因此你已经可视化了你的组件。 –

+0

由于setState导致重新渲染,所以仍然存在滞后。 – Adel

根据你与你取得的数据做什么(例如渲染项目的一长串),你可能考虑以较小的块或页面获取数据。这将允许您的组件渲染一小部分数据,并且您可以在幕后或用户与视图进行交互时加载其余部分。

不知道这是否有帮助,您可以添加加载状态,直到获取所有数据和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> 
    ); 
} 

}

+0

为什么?哦为什么?当文档明确表示要避免这种模式时,您会推荐这种方法吗? –

+0

@ sag1v你是在说加载状态?你能指点我的文件,以避免加载状态,我真的很好奇。 – pk1m

+0

@ pk1m我在讨论'componentWillMount'中的副作用。你可以在我的回答中看到它的链接。 –