web-dev-qa-db-fra.com

Comment puis-je bloquer le rendu d'un composant React jusqu'à ce que j'aie récupéré toutes les informations?

Je dois aller chercher des informations avant de rendre mon composant. Les informations seront fournies par une API et récupérées avec un appel ajax.

J'essaie juste d'attendre 10 secondes avant de rendre mon composant, mais il est écrit:

Uncaught Invariant Violation: Login.render(): A valid ReactComponent must be returned. You may have returned undefined, an array or some other invalid object.

Puis-je rendre mon composant après avoir rempli une promesse?

/** Page Login */
class Login extends React.Component {

  /**
   * @constructor
   * @param {object} props La fonction super() appelle le parent pour y transmettre ses propriétés
   */
  constructor(props) {
    super(props);

    this.handleFormSubmit = this.handleFormSubmit.bind(this);
  }

  /**
   * Reçoit les valeurs des formulaires
   */
  handleFormSubmit(data) {
    const { dispatch } = this.props;

    dispatch(fetchLoginAuth(data));
  }

  normalRender() {
    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>
    );
  }

  /**
   * Render le component - ReactTransitionGroup
   * @return {JSX} Rend la page Registration
   */
  render() {
    setTimeout(this.normalRender, 10000);
  }
}

J'utilise ES6 avec JSX, Redux, un routeur universel avec react-router.

Merci beaucoup pour votre aide!

18
Crak_mboutin

Voici ce que je fais normalement:

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
        };
    }
    componentWillMount() {
        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>
        );
    }
}

Voici un aperçu de ce qui va se passer ...

  1. Le composant va se charger
  2. composantWillMount () se déclenche, exécute loadData ()
  3. loadData () démarre la requête ajax, retourne avant la requête ajax, car nous aimons les chargements de données asynchrones
  4. render () s'exécute. Puisque this.state.data est null, nous avons passé dans le bloc if, et <div /> est renvoyé.
  5. Le chargement de données Ajax se termine et un appel this.setState() est effectué, ce qui oblige à effectuer un nouveau rendu.
  6. render () s'exécute à nouveau. Puisque this.state.data contient une valeur maintenant, nous ignorons le bloc if et rendons nos trucs normaux.
22
molson504x

Toujours laisser React rendre.

Pendant que vous faites quelque chose d'asynchrone, montrez un spinner de chargement ou quelque chose.

render() {
  <div>
    { this.state.isLoading &&
    <div>Loading.. please wait!</div>
    }
    { !this.state.isLoading &&
    <div>My data has arrived!</div>
    }
  </div>
}
10
azium

Suspendre le rendu semble hacky ...

Pourquoi ne pas restituer une partie de votre composant avec un sous-composant placeholder ... Puis, lorsque l'appel ajax se termine, déclenchez une action pour modifier l'état et restituer votre composant d'origine.

Ça va être mieux en termes d'expérience utilisateur et d'élégance.

2
DougieHauser

J'aurais juste fait un commentaire sur la première réponse, mais je n'ai pas la réputation. 

composantWillMount () est maintenant obsolète. Il est donc préférable de déplacer l'appel loadData vers le constructeur

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-menu">
            <section className="site-menu-content">
                {data}
            </section>
        </div>
    )
 }
1
Louis

Vous pouvez essayer quelque chose comme

/** Page Login */
class Login extends React.Component {
    constructor(props) {
    ...
      this.state = {
        ready: false
      };
    }
    componentWillMount() {
       setTimeout(this.handleLoading, 10000);
    }
    handleLoading() {
      this.setState({ ready: true });
    }
    render() {
       if(!this.state.ready)
         return null;
       return normalRender();
    }
}
0
Franco Risso