2015-07-30 3 views
31

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

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 
) 

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

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

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

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

+0

Чтобы гарантировать, что данные ответа отформатированы так, как вы хотите, вы должны добавить 'dataType:" json "' в запрос ajax. Кроме того, вы можете попробовать также зарегистрировать новое состояние, поскольку оно получено в 'shouldComponentUpdate'. И, наконец, проверьте, что - если есть - свойства находятся на 'index.order'. Возможно, он содержит строку вместо свойств. –

ответ

25

Вот ваш код переработан с некоторыми комментариями модифицированных частей

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

Даже не уверен, что такое решающий код здесь, но я думаю, что это была функция 'componentWillMount'. AJAX теперь загружает все содержимое с сервера. Не имеет для меня такого смысла, но я буду изучать его больше, хотя сейчас все работает. –

+0

@TomJoad 'componentWillReceiveProps', как задокументировано, не вызвано первоначальным рендерингом, поэтому важно также использовать свойство' componentWillMount' –

+0

, чтобы определить неопределенное причину ошибки. Но спасибо за подсказку, в которой я закончил использование свойства flag 'loaded: false' –

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

OP использует полученные реквизиты в своих данных запроса ajax. Этот ответ не работает для него. –

+1

Перемещение Ajax в любое из 'will' или' did' mount не решит проблему; до завершения вызова Ajax почти наверняка будет один рендер. –

0

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

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

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

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

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

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

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

Смежные вопросы