0

Как заблокировать рендеринг React-компонента до завершения загрузки всех данных?

12

Я пытаюсь получить некоторые данные перед рендерингом моего компонента. Эти данные будут предоставлены 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.

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

/** Страница Вход */
class Login extends React.Component {

  /**
   * @constructor
   * @param {object} props Конструктор вызывает родительский класс для передачи его свойств
   */
  constructor(props) {
    super(props);

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

  /**
   * Обрабатывает значения формы
   */
  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="Логотип" /></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>
    );
  }

  /**
   * Рендеринг компонента - ReactTransitionGroup
   * @return {JSX} Отрисовывает страницу Входа
   */
  render() {
    setTimeout(this.normalRender, 10000);
  }
}

Я использую ES6 с JSX, Redux и универсальный роутер с react-router.

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

4 ответ(ов)

0

Всегда позволяйте React выполнять рендеринг.

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

render() {
  return (
    <div>
      {this.state.isLoading ? (
        <div>Загрузка... пожалуйста, подождите!</div>
      ) : (
        <div>Мои данные уже поступили!</div>
      )}
    </div>
  );
}

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

0

Обновление: Этот ответ устарел. Лучше отказаться от классов и использовать хуки.

#######################

Вот альтернативный способ, отличающийся от принятого ответа с использованием конструктора. Лично я нахожу этот подход немного более чистым.

class Menu extends Component {

    state = {}

    constructor(props) {
        super(props);
        loadData().then(data =>
            this.setState({data: data})
        );
    }

    async loadData() {
        // Получаем ваши данные
    }

    render() {
        if (isEmpty(this.state)) {
            return <div>Loading</div>;
        }
        return (
            <div id="site">
                {data}
            </div>
        );
    }
}

Советую рассмотреть использование хуков и функциональных компонентов, так как это современный подход в React, который делает код более понятным и лаконичным.

0

Вы можете попробовать что-то вроде следующего:

/** Страница Входа */
class Login extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            ready: false
        };
    }

    componentWillMount() {
        setTimeout(this.handleLoading.bind(this), 10000);
    }

    handleLoading() {
        this.setState({ ready: true });
    }

    render() {
        if (!this.state.ready) {
            return null;
        }
        return normalRender(); // Здесь должен быть ваш метод рендеринга
    }
}

Обратите внимание, что я добавил вызов super(props) в конструкторе и использовал bind(this) для метода handleLoading, чтобы сохранить контекст this. Также учтите, что метод componentWillMount считается устаревшим (deprecated) и рекомендуется использовать componentDidMount вместо него для инициализации.

0

Приостановка рендера может показаться хаком...

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

Такое решение будет лучше как с точки зрения пользовательского опыта (UX), так и с точки зрения элегантности.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь