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

时间:2017-09-25 17:45:09

标签: javascript reactjs react-native

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

更新: 如果我想使用加载指示器,我应该把它放在哪里?我使用承诺来获取这样的数据:

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

3 个答案:

答案 0 :(得分:10)

您绝不应在componentWillMountconstructor中使用异步操作 而是在componentDidMount中进行 您可以在DOCS

中阅读相关内容
  

在此方法中同步设置状态不会触发a   重新渲染。避免引入任何副作用或订阅   这种方法。

修改
作为更新后问题的后续内容

  

如果我想使用加载指示器,我应该把它放在哪里?我用了一个   承诺获取我的数据

我做了一个小例子,在获取数据的同时获取数据和渲染加载器。

在此示例中,我使用名为jsonplaceholder的免费API测试程序,我正在使用此URL来获取用户的一些随机数据。
您可以看到我使用contructor的空数组初始化users
中的状态,我在componentDidMount生命中获取用户循环方法并更新已返回的promise的回调内的状态的users数组。请注意,我在setTimeOut方法中执行此操作以获得2秒的延迟。

现在,React不会真的等待我们的ajax请求返回结果,无论如何都会调用render方法,因此在运行的生命周期方法中执行ajax请求在render方法之前(如componentWillMountconstructor)不是上面提到的最佳做法,这就是我们在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)

根据您对提取的数据执行的操作(例如,呈现一长串项目),您可以考虑以较小的块或页面提取数据。这将允许您的组件呈现一小组数据,您可以在幕后加载其余内容,或者在用户与视图交互时加载。