2015-07-30 5 views
31

Mam problem z jednym z moich komponentów React. Myślę, że AJAX nie ładuje całej zawartości z zewnętrznego serwera, zanim React wyświetli komponent ChildComp.Jak czekać na odpowiedź AJAX i dopiero po tym renderować komponent?

Obj tree

Powyżej można zobaczyć drzewo odpowiedzi, która jest pochodzących z serwera. A to jest mój kod komponentu:

var ChildComp = React.createClass({ 
    getInitialState: function(){ 
    return { 
     response: [{}] 
    } 
    }, 
    componentWillReceiveProps: function(nextProps){ 
    var self = this; 

    $.ajax({ 
     type: 'GET', 
     url: '/subscription', 
     data: { 
     subscriptionId: nextProps.subscriptionId //1 
     }, 
     success: function(data){ 
     self.setState({ 
      response: data 
     }); 
     console.log(data); 
     } 
    }); 
    }, 
    render: function(){ 
     var listSubscriptions = this.state.response.map(function(index){ 
     return (
      index.id 
     ) 
     }); 
     return (
     <div> 
      {listSubscriptions} 
     </div> 
    ) 
    } 
}); 

To działa dobrze, ale jeśli mogę zmienić swój powrót do:

return (
    index.id + " " + index.order.id 
) 

id jest niezdefiniowany. I wszystkie inne właściwości przedmiotu zamówienia. Dlaczego tak jest? Jeśli I console.log moja odpowiedź po success funkcji daje wszystkie dane (jak na zdjęciu). Domyślam się, że tylko pierwsze obiekty są ładowane, gdy React renderuje komponent, a następnie ładowane są wszystkie inne wewnętrzne obiekty. Nie mogę powiedzieć, czy tak jest (brzmi tak dziwnie), ani nie mogę powiedzieć, jak rozwiązać ten problem.

Próbowałem też coś jak ten

success: function(data){ 
    self.setState({ 
     response: data 
    }, function(){ 
     self.forceUpdate(); 
    }) 
    }.bind(this) 

ale nadal ponowne renderowanie dzieje się przed moim console.log(data) jest wyzwalany. Jak czekać na odpowiedź AJAX i dopiero po tym renderować komponent?

+0

Aby upewnić się, że dane odpowiedzi są sformatowane zgodnie z potrzebami, należy dodać parametr 'dataType:" json "' do żądania ajax. Możesz także spróbować zarejestrować nowy stan, jaki został odebrany w 'shouldComponentUpdate'. I na koniec sprawdź, jakie - jeśli jakiekolwiek - właściwości znajdują się na 'index.order'. Być może zawiera ciąg znaków zamiast właściwości. –

Odpowiedz

25

Oto twój kod przerobione z pewnymi uwagami zmodyfikowanych części

getInitialState: function(){ 
    return { 
     // [{}] is weird, either use undefined (or [] but undefined is better). 
     // If you use [], you loose the information of a "pending" request, as 
     // you won't be able to make a distinction between a pending request, 
     // and a response that returns an empty array 
     response: undefined 
    } 
    }, 

loadSubscriptionData: function(subscriptionId){ 
    var self = this; 

    // You probably want to redo the ajax request only when the 
    // subscriptionId has changed (I guess?) 
    var subscriptionIdHasChanged = 
     (this.props.subscriptionId !== subscriptionId) 

    if (subscriptionIdHasChanged) { 
     // Not required, but can be useful if you want to provide the user 
     // immediate feedback and erase the existing content before 
     // the new response has been loaded 
     this.setState({response: undefined}); 

     $.ajax({ 
      type: 'GET', 
      url: '/subscription', 
      data: { 
      subscriptionId: subscriptionId //1 
      }, 
      success: function(data){ 

      // Prevent concurrency issues if subscriptionId changes often: 
      // you are only interested in the results of the last ajax  
      // request that was made. 
      if (self.props.subscriptionId === subscriptionId) { 
       self.setState({ 
        response: data 
       }); 
      } 
      } 
     }); 
    } 
    }, 

// You want to load subscriptions not only when the component update but also when it gets mounted. 
    componentDidMount: function(){ 
    this.loadSubscriptionData(this.props.subscriptionId); 
    }, 
    componentWillReceiveProps: function(nextProps){ 
    this.loadSubscriptionData(nextProps.subscriptionId); 
    }, 

render: function(){ 

     // Handle case where the response is not here yet 
     if (!this.state.response) { 
     // Note that you can return false it you want nothing to be put in the dom 
     // This is also your chance to render a spinner or something... 
     return <div>The responsive it not here yet!</div> 
     } 

     // Gives you the opportunity to handle the case where the ajax request 
     // completed but the result array is empty 
     if (this.state.response.length === 0) { 
      return <div>No result found for this subscription</div>; 
     } 


     // Normal case   
     var listSubscriptions = this.state.response.map(function(index){ 
     return (
      index.id 
     ) 
     }); 
     return (
     <div> 
      {listSubscriptions} 
     </div> 
    ) 
    } 
+0

Nie jestem nawet pewien, co było tutaj kodem do rozwiązania, ale domyślam się, że była to funkcja 'componentWillMount'. AJAX ładuje teraz całą zawartość z serwera. Nie ma to dla mnie większego sensu, ale zajrzę do niego jeszcze bardziej, chociaż wszystko działa teraz. –

+0

@TomJoad 'componentWillReceiveProps' jest, jak dokumentowano, nie jest wywoływane podczas początkowego renderowania, dlatego ważne jest, aby również używać właściwości' componentWillMount' –

+0

ustawiać właściwość stanu na niezdefiniowaną przyczynę błędu. Ale dzięki za podpowiedź i skończyłem używając właściwości flagi 'loaded: false' –

2

Po pierwsze, chcesz umieścić wywołanie AJAX wewnątrz componentWillMount() lub componentDidMount() (jeśli potrzebujesz wykonać trochę manipulacji DOM).

componentWillReceiveProps() jest

Wywołuje się, gdy składnik odbiera nowe rekwizyty. Ta metoda nie jest wywoływana dla początkowego renderowania.

https://facebook.github.io/react/docs/component-specs.html

Ale nawet jeśli umieścić wywołanie AJAX wewnątrz componentWillMount() lub componentDidMount(), render() prawdopodobnie zostanie wywołana z pustym odpowiedzi przed wykończeń AJAX połączeń.

Więc kolejność wywołania byłoby tak (Przypuszczałem umieścić wywołanie AJAX wewnątrz componentWillMount():

componentWillMount() (AJAX wywołanie jest wyzwalany) ->componentDidMount() ->render() (render z pustym odpowiedzi, więc index.order.id będzie powodować błąd) -> AJAX zwraca i dzwoni this.setState() -> (niektóre inne metody cyklu życia komponentu, takie jak shouldComponentUpdate() są wywoływane, zobacz łącze FB powyżej, aby uzyskać szczegółowe informacje) ->render() jest wywoływany ponownie.

więc rozwiązanie problemu jest:

1) Przeniesienie wywołania AJAX albo componentWillMount() lub componentDidMount()

2) albo ustawić stan początkowy tylko [] (w przeciwieństwie do [{}]) lub sprawdzić, czy index jest pusta w

var listSubscriptions = this.state.response.map(function(index){ 
    return (
    index.id 
) 
}); 
+1

OP wykorzystuje otrzymane rekwizyty w swoich danych żądania ajax. Ta odpowiedź nie działa dla niego. –

+1

Przeniesienie Ajaxa na mount 'will' lub' did' nie rozwiąże problemu; prawie na pewno będzie jeden render przed zakończeniem połączenia Ajax. –

0

wrzuciłem go do JSBin i nie wydaje się odtworzyć problem.

http://jsbin.com/jefufe/edit?js,output

każdym razem kliknięciu przycisku, wysyła nowe rekwizyty do <ChildComp/>, które następnie wywołuje jego componentWillReceiveProps, wysyła (fake) żądania ajax, odbiera (fake) odpowiedzi, ustawia stan odpowiedzią na (fałszywa) odpowiedź i ponownie renderuje komponent z przedstawionymi poprawnymi danymi.

Jedyną rzeczą, którą dodałem, był czek na index.id == null, zanim spróbuję uzyskać do niego dostęp, ale nie sądzę, że to pomoże rozwiązać problem, który dotyczy braku dostępu do index.order.id.

Jedyny powód, dla którego mogę wymyślić, dlaczego nie możesz uzyskać dostępu do index.order.id, może dlatego, że masz jakiś kod, który nam nie pokazuje, co modyfikuje odpowiedź w pewnym momencie.

Pamiętaj też, że komponenty React zawsze będą ponownie renderowane po zmianie stanu lub rekwizytów, chyba że zdefiniowałeś zachowanie w shouldComponentUpdate, które mówi inaczej. Oznacza to, że po otrzymaniu całego obiektu z serwera i ustawieniu go na stan w komponencie, ponownie wyświetli się i wyświetli zaktualizowany stan. Nie ma powodu, dla którego React mógłby "płytko renderować" obiekt odpowiedzi, tak jak sugerowałeś w swoim oryginalnym wpisie.

Nie jestem pewien, czy ta odpowiedź pomaga, ale mam nadzieję, że możesz spojrzeć na dostarczony JSBin i znaleźć coś, co mógłbyś przeoczyć.

Powiązane problemy