web-dev-qa-db-ja.com

AJAX応答を待機し、その後コンポーネントをレンダリングする方法のみ?

私のReactコンポーネントの1つに問題があります。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は未定義です。そして、注文オブジェクトの他のすべてのプロパティ。なぜこんな感じ? success関数の後の応答が_console.log_の場合、すべてのデータが表示されます(図のように)。私の推測では、Reactがコンポーネントをレンダリングし、その後、他のすべての内部オブジェクトがロードされた場合、最初のオブジェクトのみがロードされます。これを解決する方法を言うことはできません。

私もこのようなものを試しました

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

ただし、console.log(data)がトリガーされる前に再レンダリングが行われます。 AJAX応答を待機し、その後コンポーネントをレンダリングする方法のみ?

37
Tom Joad

変更された部分のコメントを使用してコードを修正しました

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

まず、AJAX呼び出しをcomponentWillMount()またはcomponentDidMount()のいずれかに配置する必要があります(DOM操作が必要な場合)。

componentWillReceiveProps()

コンポーネントが新しい小道具を受け取ったときに呼び出されます。 このメソッドは、初期レンダーでは呼び出されません

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

ただし、AJAX呼び出しをcomponentWillMount()またはcomponentDidMount()内に配置しても、AJAX呼び出しが完了する前に、おそらくrender()が空の応答で呼び出されます。 。

したがって、呼び出しの順序は次のようになります(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
Brian Park

私はそれをJSBinに投げましたが、あなたの問題を再現できないようです。

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

ボタンがクリックされるたびに、<ChildComp/>componentWillReceivePropsをトリガーし、(偽の)ajaxリクエストを送信し、(偽の)応答を受信し、応答状態を(偽の)応答に設定し、正しいデータでコンポーネントを再レンダリングします示されています。

追加したのは、index.id == nullアクセスしようとする前に、それはあなたがアクセスできないことについてあなたが抱えている問題を解決するために何もしないと思うindex.order.id

アクセスできない理由について私が思いつく唯一の理由はindex.order.idは、おそらく、ある時点で応答を変更するコードを表示していないためです。

また、Reactコンポーネントは、その状態またはプロップが変更されたときに、別の方法でshouldComponentUpdateで動作を定義していない限り、常に再レンダリングされます。サーバーからオブジェクト全体を取得し、コンポーネントの状態に設定すると、再レンダリングされ、更新された状態が表示されます。Reactが「浅くレンダリング」する理由/方法はありません元の投稿で提案していたような応答オブジェクト。

この答えが役立つかどうかはわかりませんが、できれば、私が提供したJSBinを見て、見落としているかもしれないものを見つけてください。

0
Michael Parker

AJAX componentDidMount()の呼び出しを行うだけで、レンダリング時に簡単に確認できます...

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

// do your stuff here

}

または、react 16.6以上を使用している場合は、単純に遅延コンポーネントを使用できます。

0