Подтвердить что ты не робот

Как ждать ответа AJAX и только после этого сделать компонент?

У меня проблема с одной из моих компонентов React. Я думаю, что AJAX не загружает весь контент с внешнего сервера, прежде чем React отобразит компонент ChildComp.

Obj tree

Выше вы можете увидеть дерево ответа, которое поступает с сервера. И это мой код компонента:

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

Это работает отлично, но если я изменю свое возвращение на:

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

id undefined. И все остальные свойства объекта заказа. Почему так? Если я console.log мой ответ после функции success, он дает все данные (например, на картинке). Я предполагаю, что только первые объекты загружаются, когда React отображает компонент, и после этого загружаются все остальные внутренние объекты. Я не могу сказать, если это случай (звучит так странно), и я не могу сказать, как это решить.

Я тоже попробовал что-то вроде этого

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

но повторная визуализация выполняется до того, как будет запущен мой console.log(data). Как ждать ответа AJAX и только после этого визуализировать компонент?

4b9b3361

Ответ 1

Здесь ваш код переработан с некоторыми комментариями измененных частей

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

Ответ 2

Прежде всего, вы хотите поместить вызов AJAX внутри componentWillMount() или componentDidMount() (если вам нужно сделать некоторые манипуляции с DOM).

componentWillReceiveProps() является

Вызывается, когда компонент получает новые реквизиты. Этот метод не вызывается для исходного рендеринга.

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

Но даже если вы поместите вызов AJAX внутри componentWillMount() или componentDidMount(), render(), вероятно, будет вызван с пустым ответом до завершения вызова AJAX.

Таким образом, порядок вызова будет таким (я предположил, что вы поместили вызов AJAX внутри componentWillMount():

componentWillMount() (вызов AJAX запускается) → componentDidMount()render() (рендеринг с пустым ответом, поэтому index.order.id приведет к ошибке) → Возврат AJAX и вызовы this.setState() → ( вызывается несколько других методов жизненного цикла компонента, таких как shouldComponentUpdate(), см. ссылку FB выше для деталей) → render() вызывается снова.

Итак, решение вашей проблемы:

1) Перемещение вызова AJAX на componentWillMount() или componentDidMount()

2) Либо установите начальное состояние на [] (в отличие от [{}]), либо проверьте, что index пуст в

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

Ответ 3

Я бросил его в JSBin, и я не могу воспроизвести вашу проблему.

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

При каждом нажатии кнопки он отправляет новые реквизиты в <ChildComp/>, который затем запускает свой componentWillReceiveProps, отправляет (фальшивый) запрос ajax, получает (фальшивый) ответ, устанавливает состояние ответа на (поддельный ) и повторно отображает компонент с указанными правильными данными.

Единственное, что я добавил, это проверка на index.id == null, прежде чем пытаться получить к нему доступ, но я не думаю, что это что-то для решения проблемы, с которой вы сталкиваетесь, о невозможности доступа к index.order.id.

Единственная причина, по которой я могу понять, почему вы не можете получить доступ к index.order.id, состоит в том, что, возможно, у вас есть код, который вы не показываете нам, который в какой-то момент меняет ответ.

Кроме того, помните, что компоненты React всегда будут повторно отображаться, когда изменилось его состояние или реквизит, если вы не определили поведение в shouldComponentUpdate, которое говорит иначе. Это означает, что, когда вы получаете весь объект с сервера и устанавливаете его в состояние в своем компоненте, он будет повторно отображать и показывать вам обновленное состояние. Для React нет причины/способа "мелко визуализировать" ваш объект ответа, как вы предлагали в своем исходном сообщении.

Я не уверен, помогает ли этот ответ, но, надеюсь, вы можете посмотреть JSBin, который я предоставил, и найти то, что вы могли упустить.

Ответ 4

Просто выполните AJAX-вызов в componentDidMount() и в рендере вы можете просто проверить...

if(this.state.response === '' || this.state.response === [] || this.state.response === undefined)
return <Loader />
else{

// do your stuff here

}

ИЛИ если вы используете реакцию 16.6 или более, вы можете просто использовать ленивые компоненты.