Как я могу заблокировать компонент React, который будет отображаться, пока я не получал всю информацию?

Мне нужно получить некоторую информацию перед рендерингом моего компонента. Информация будет предоставлена ​​API и получена с помощью вызова ajax.

Я просто пытаюсь подождать 10 секунд до рендеринга моего компонента, но он говорит:

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

Могу ли я представить свой компонент после выполнения обещания?

/** 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);
  }
}

Я использую ES6 с JSX, redux, универсальным маршрутизатором с реактивным маршрутизатором.

Большое спасибо за вашу помощь!

Ответ 1

Вот что я обычно делаю:

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>
        );
    }
}

Здесь разбивка того, что должно произойти...

  1. Компонент будет загружаться
  2. componentDidMount() запускает, запускает loadData()
  3. loadData() запускает ajax-запрос, возвращает до того, как ajax-запрос возвращает данные, потому что нам нравится асинхронная загрузка данных
  4. render() запускается. Поскольку this.state.data - это null, мы перешли в блок if, и возвращается <div />.
  5. Загрузка данных Ajax заканчивается, и выполняется вызов this.setState(), который вызывает повторную визуализацию.
  6. render() запускается снова. Поскольку this.state.data теперь содержит значение, мы пропускаем блок if и отрисовываем наши обычные вещи.

Изменить (11 октября 2019 г.): перенесен componentWillMount() в componentDidMount()

Ответ 2

Всегда разрешать React render.

Пока вы делаете что-то асинхронное, покажите загрузочный счетчик или что-то еще.

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

Ответ 3

Вы можете попробовать что-то вроде

/** 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();
    }
}

Ответ 4

Приостановка рендера кажется взломанной...

Почему бы не отобразить часть вашего компонента с каким-то субподписчиком-заполнителем. а затем, когда заканчивается вызов ajax, запустите действие, чтобы изменить состояние и отобразить исходный компонент.

Это будет лучше как с точки зрения UX, так и изящества.

Ответ 5

Я бы просто прокомментировал это, но у меня нет репутации.

componentWillMount() теперь устарел. Так что лучше переместить вызов loadData в конструктор

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>
    )
 }