How can I block a React component to be rendered until I fetched all informations?
Here's what I do normally:
class Login extends React.Component { constructor(props) { //IMPLEMENT OTHER JUNK HERE this.state = { data: null //This is what our data will eventually be loaded into }; } componentDidMount() { this.loadData(); } loadData() { /*LOAD DATA, INSERT BELOW LINE IN CALLBACK FUNCTION this.setState({ data: //LOADED DATA }); */ } render() { if (!this.state.data) { return <div /> } //WE HAVE DATA, DO A NORMAL RENDER return ( <div id="login-page"> <div className="container-fluid"> <div className="row"> <div className="col-md-2"> <Link to="/" className="home-link"><img src={BASE_URL + '/assets/img/logo.svg'} alt="Logo" /></Link> </div> </div> <div className="row"> <div className="col-lg-4 col-lg-offset-4"> <h1><FormattedMessage {...messages.loginPageTitle} /></h1> </div> </div> {React.cloneElement(this.props.children || <div />, { onSubmit: this.handleFormSubmit, login: this.props.login })} </div> </div> ); }}
Here's a breakdown of what is going to happen...
- Component is going to load
- componentDidMount() fires, runs loadData()
- loadData() starts ajax request, returns before ajax request returns data because we love asynchronous data loads
- render() runs. Since
this.state.data
isnull
, we have pass into the if block, and<div />
is returned. - Ajax data load finishes, and a
this.setState()
call is made, which forces a re-render. - render() runs again. Since
this.state.data
contains a value now, we skip over the if block and render our normal stuff.
Edit (11 Oct 2019): Migrated componentWillMount() to componentDidMount()
Always let React render.
While you're doing something asynchronous, show a loading spinner or something.
render() { <div> { this.state.isLoading && <div>Loading.. please wait!</div> } { !this.state.isLoading && <div>My data has arrived!</div> } </div>}
An alternative way to the accepted answer using the constructor. Personally I find this a little cleaner.
class Menu extends Component {state = {}constructor(props) { super(props) loadData().then(data => this.setState({data: data}) )}async loadData() { //get your data}render() { if (isEmpty(this.state)) { return <div>Loading</div> } return ( <div id="site"> {data} </div> ) }