Reactjs asynchroniczne renderowanie komponentów

83

Chcę wyrenderować mój komponent po zakończeniu mojego żądania Ajax.

Poniżej możesz zobaczyć mój kod

var CategoriesSetup = React.createClass({

    render: function(){
        var rows = [];
        $.get('http://foobar.io/api/v1/listings/categories/').done(function (data) {
            $.each(data, function(index, element){
                rows.push(<OptionRow obj={element} />);
            });
           return (<Input type='select'>{rows}</Input>)

        })

    }
});

Ale otrzymuję poniższy błąd, ponieważ zwracam renderowanie w ramach gotowej metody mojego żądania Ajax.

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

Czy istnieje sposób, aby poczekać na zakończenie żądania AJAX przed rozpoczęciem renderowania?

Tuńczyk
źródło
3
Również mały chwytak, ale pobieranie danych w procedurze render () nie jest naprawdę dobrym pomysłem. Zachowaj render () do renderowania i wyodrębnij resztę. Ponadto możesz chcieć uzyskać te dane tylko raz, a nie za każdym razem, gdy komponent jest renderowany.
Phil Cooper

Odpowiedzi:

132

Istnieją dwa sposoby, aby sobie z tym poradzić, a wybór zależy od tego, który składnik powinien być właścicielem danych i stanu ładowania.

  1. Przenieś żądanie Ajax do elementu nadrzędnego i warunkowo wyrenderuj komponent:

    var Parent = React.createClass({
      getInitialState: function() {
        return { data: null };
      },
    
      componentDidMount: function() {
        $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {
          this.setState({data: data});
        }.bind(this));
      },
    
      render: function() {
        if (this.state.data) {
          return <CategoriesSetup data={this.state.data} />;
        }
    
        return <div>Loading...</div>;
      }
    });
    
  2. Zachowaj żądanie Ajax w komponencie i renderuj coś innego warunkowo podczas ładowania:

    var CategoriesSetup = React.createClass({
      getInitialState: function() {
        return { data: null };
      },
    
      componentDidMount: function() {
        $.get('http://foobar.io/api/v1/listings/categories/').done(function(data) {
          this.setState({data: data});
        }.bind(this));
      },
    
      render: function() {
        if (this.state.data) {
          return <Input type="select">{this.state.data.map(this.renderRow)}</Input>;
        }
    
        return <div>Loading...</div>;
      },
    
      renderRow: function(row) {
        return <OptionRow obj={row} />;
      }
    });
    
Michelle Tilley
źródło
6
Natknąłem się na tę odpowiedź w 2017 roku, czy te dwa najlepsze rozwiązania nadal są najlepsze w użyciu?
Dan
@Dan React renderuje interfejs użytkownika na podstawie właściwości i stanu, więc podstawowa koncepcja pozostanie taka sama - wykonaj żądanie Ajax, ustaw stan i ponownie wyrenderuj coś. Jednak wzorce, takie jak komponenty wyższego rzędu, stały się bardziej popularne, pokazując, jak można odciąć się od złożoności.
Michelle Tilley
1
if (this.state.data)powinno być, if (this.state && this.state.data)ponieważ czasami stan może być zerowy.
Timo
@Timo Hm, w jakim przypadku by to this.statebyło null?
Michelle Tilley
6
@Timo zainicjuj stan w konstruktorze
Louis
8

Podstawowy przykład asynchronicznego renderowania komponentów znajduje się poniżej:

import React                from 'react';
import ReactDOM             from 'react-dom';        
import PropTypes            from 'prop-types';

export default class YourComponent extends React.PureComponent {
    constructor(props){
        super(props);
        this.state = {
            data: null
        }       
    }

    componentDidMount(){
        const data = {
                optPost: 'userToStat01',
                message: 'We make a research of fetch'
            };
        const endpoint = 'http://example.com/api/phpGetPost.php';       
        const setState = this.setState.bind(this);      
        fetch(endpoint, {
            method: 'POST',
            body: JSON.stringify(data)
        })
        .then((resp) => resp.json())
        .then(function(response) {
            setState({data: response.message});
        });
    }

    render(){
        return (<div>
            {this.state.data === null ? 
                <div>Loading</div>
            :
                <div>{this.state.data}</div>
            }
        </div>);
    }
}
rzymski
źródło