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...

  1. Component is going to load
  2. componentDidMount() fires, runs loadData()
  3. loadData() starts ajax request, returns before ajax request returns data because we love asynchronous data loads
  4. render() runs. Since this.state.data is null, we have pass into the if block, and <div /> is returned.
  5. Ajax data load finishes, and a this.setState() call is made, which forces a re-render.
  6. 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>
    )
 }