2015-05-09 83 views
40

我與reactjs工作似乎並不能試圖顯示JSON數據(來自文件或服務器)時,爲防止此錯誤:陣營JS - 遺漏的類型錯誤:this.props.data.map不是一個函數

Uncaught TypeError: this.props.data.map is not a function 

我看:

React code throwing 「TypeError: this.props.data.map is not a function」

React.js this.props.data.map() is not a function

這些都不幫我解決這個問題。在我的頁面加載後,我可以驗證this.data.props不是未定義的(並且具有與json對象相同的值 - 可以使用window.foo調用),因此它看起來好像在調用它時沒有及時加載由ConversationList。我如何確保map方法適用於json數據而不是undefined變量?

var converter = new Showdown.converter(); 

var Conversation = React.createClass({ 
    render: function() { 
    var rawMarkup = converter.makeHtml(this.props.children.toString()); 
    return (
     <div className="conversation panel panel-default"> 
     <div className="panel-heading"> 
      <h3 className="panel-title"> 
      {this.props.id} 
      {this.props.last_message_snippet} 
      {this.props.other_user_id} 
      </h3> 
     </div> 
     <div className="panel-body"> 
      <span dangerouslySetInnerHTML={{__html: rawMarkup}} /> 
     </div> 
     </div> 
    ); 
    } 
}); 

var ConversationList = React.createClass({ 
    render: function() { 

    window.foo   = this.props.data; 
    var conversationNodes = this.props.data.map(function(conversation, index) { 

     return (
     <Conversation id={conversation.id} key={index}> 
      last_message_snippet={conversation.last_message_snippet} 
      other_user_id={conversation.other_user_id} 
     </Conversation> 
    ); 
    }); 

    return (
     <div className="conversationList"> 
     {conversationNodes} 
     </div> 
    ); 
    } 
}); 

var ConversationBox = React.createClass({ 
    loadConversationsFromServer: function() { 
    return $.ajax({ 
     url: this.props.url, 
     dataType: 'json', 
     success: function(data) { 
     this.setState({data: data}); 
     }.bind(this), 
     error: function(xhr, status, err) { 
     console.error(this.props.url, status, err.toString()); 
     }.bind(this) 
    }); 
    }, 
    getInitialState: function() { 
    return {data: []}; 
    }, 
    componentDidMount: function() { 
    this.loadConversationsFromServer(); 
    setInterval(this.loadConversationsFromServer, this.props.pollInterval); 
    }, 
    render: function() { 
    return (
     <div className="conversationBox"> 
     <h1>Conversations</h1> 
     <ConversationList data={this.state.data} /> 
     </div> 
    ); 
    } 
}); 

$(document).on("page:change", function() { 
    var $content = $("#content"); 
    if ($content.length > 0) { 
    React.render(
     <ConversationBox url="/conversations.json" pollInterval={20000} />, 
     document.getElementById('content') 
    ); 
    } 
}) 

編輯:加樣conversations.json

注 - 調用this.props.data.conversations也將返回一個錯誤:

var conversationNodes = this.props.data.conversations.map... 

回報

Uncaught TypeError: Cannot read property 'map' of undefined

這裏是conversations.json:

{ 「user_has_unread_messages」:假 「unread_messages_count」:0, 「會話」:[{ 「ID」:18768 「last_message_snippet」: 「Lorem存有」, 「other_user_id」:10193}]}

+0

可以嗎發佈示例conversations.json? – user120242

+0

已添加。非常感謝! – keypulsations

+0

我已經更新了答案。此外,還有一些建議:向ConversationList添加propTypes:{data:React.PropTypes.array}以驗證數據的類型,並在ConversationBox中添加一個componentWillUnmount:fn(),即「clearInterval」間隔。 – user120242

回答

61

.map函數僅適用於陣列。
它看起來像data是不是你期待它的格式(它是{}但你期待[])。

this.setState({data: data}); 

應該

this.setState({data: data.conversations}); 

檢查什麼類型的「數據」被設置爲,並確保它是一個數組。

與一些建議(propType驗證和clearInterval)修改後的代碼:

var converter = new Showdown.converter(); 

var Conversation = React.createClass({ 
    render: function() { 
    var rawMarkup = converter.makeHtml(this.props.children.toString()); 
    return (
     <div className="conversation panel panel-default"> 
     <div className="panel-heading"> 
      <h3 className="panel-title"> 
      {this.props.id} 
      {this.props.last_message_snippet} 
      {this.props.other_user_id} 
      </h3> 
     </div> 
     <div className="panel-body"> 
      <span dangerouslySetInnerHTML={{__html: rawMarkup}} /> 
     </div> 
     </div> 
    ); 
    } 
}); 

var ConversationList = React.createClass({ 
// Make sure this.props.data is an array 
    propTypes: { 
    data: React.PropTypes.array.isRequired 
    }, 
    render: function() { 

    window.foo   = this.props.data; 
    var conversationNodes = this.props.data.map(function(conversation, index) { 

     return (
     <Conversation id={conversation.id} key={index}> 
      last_message_snippet={conversation.last_message_snippet} 
      other_user_id={conversation.other_user_id} 
     </Conversation> 
    ); 
    }); 

    return (
     <div className="conversationList"> 
     {conversationNodes} 
     </div> 
    ); 
    } 
}); 

var ConversationBox = React.createClass({ 
    loadConversationsFromServer: function() { 
    return $.ajax({ 
     url: this.props.url, 
     dataType: 'json', 
     success: function(data) { 
     this.setState({data: data.conversations}); 
     }.bind(this), 
     error: function(xhr, status, err) { 
     console.error(this.props.url, status, err.toString()); 
     }.bind(this) 
    }); 
    }, 
    getInitialState: function() { 
    return {data: []}; 
    }, 

/* Taken from 
    https://facebook.github.io/react/docs/reusable-components.html#mixins 
    clears all intervals after component is unmounted 
    */ 
    componentWillMount: function() { 
    this.intervals = []; 
    }, 
    setInterval: function() { 
    this.intervals.push(setInterval.apply(null, arguments)); 
    }, 
    componentWillUnmount: function() { 
    this.intervals.map(clearInterval); 
    }, 

    componentDidMount: function() { 
    this.loadConversationsFromServer(); 
    this.setInterval(this.loadConversationsFromServer, this.props.pollInterval); 
    }, 
    render: function() { 
    return (
     <div className="conversationBox"> 
     <h1>Conversations</h1> 
     <ConversationList data={this.state.data} /> 
     </div> 
    ); 
    } 
}); 

$(document).on("page:change", function() { 
    var $content = $("#content"); 
    if ($content.length > 0) { 
    React.render(
     <ConversationBox url="/conversations.json" pollInterval={20000} />, 
     document.getElementById('content') 
    ); 
    } 
}) 
+1

賓果!這是做的伎倆,非常感謝:) – keypulsations

5

更一般地,您還可以在新的數據轉換成一個數組,並使用類似CONCAT:

var newData = this.state.data.concat([data]); 
this.setState({data: newData}) 

這種模式實際上用於Facebook的ToDo演示應用程序(請參閱「應用程序」部分),地址爲https://facebook.github.io/react/

+0

這是一個整潔的提示/技巧,但我想指出,這將掩蓋真正的問題,並不會解決OP。在OP的情況下,這將無濟於事,因爲數據仍然是格式錯誤的(而不是預期的)。由於所有道具都爲空,對話很可能最終會變成空的。我建議不要使用這個技巧,除非你確定你的數據是你想要的格式,所以當返回的數據不是你認爲應該的時候,你不會以意外的行爲結束。 – user120242

1

你不需要一個數組來完成它。

var ItemNode = this.state.data.map(function(itemData) { 
return (
    <ComponentName title={itemData.title} key={itemData.id} number={itemData.id}/> 
); 
}); 
+0

這是不同的兄弟? –

0

發生這種情況是因爲組件在異步數據到達之前呈現,您應該在控制之前進行渲染。

我解決它是這樣的:當屬性爲null /未定義

render() { 
    let partners = this.props && this.props.partners.length > 0 ? 
     this.props.partners.map(p=> 
      <li className = "partners" key={p.id}> 
       <img src={p.img} alt={p.name}/> {p.name} </li> 
     ) : <span></span>; 

    return (
     <div> 
      <ul>{partners}</ul> 
     </div> 
    ); 
} 
  • 地圖解決不了,所以我做了控制第一

this.props && this.props.partners.length > 0 ?

相關問題