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