React JS-Uncaught TypeError: это.реквизит.данные.карта-это не функция
Я работаю с reactjs и не могу предотвратить эту ошибку при попытке отобразить данные json (либо из файла, либо с сервера):
Uncaught TypeError: this.props.data.map is not a function
Я посмотрел на:
React code throwing " TypeError: this.реквизит.данные.карта-это не функция"
реагировать.Яш это.реквизит.данные.map () - это не функция
ни один из них помог мне решить эту проблему. После загрузки страницы, я могу проверить, что это.данные.реквизит не является неопределенным (и имеет значение эквивалентно объекту 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')
);
}
})
изменить: добавление примеров разговоров.json
Примечание - вызов this.props.data.conversations
также возвращает ошибку:
var conversationNodes = this.props.data.conversations.map...
возвращает
Uncaught TypeError: Cannot read property 'map' of undefined
здесь разговоры.json:
{"user_has_unread_messages":false,"unread_messages_count":0,"conversations":[{"id":18768,"last_message_snippet":"Lorem ipsum", "other_user_id": 10193}]}
6 ответов:
The
.map
функция доступна только на массиве.
Это выглядит какdata
не в том формате, который вы ожидаете (это {}, но вы ожидаете []).this.setState({data: data});
должно быть
this.setState({data: data.conversations});
проверьте, какой тип "данные" устанавливается, и убедитесь, что это массив.
модифицированный код с несколькими рекомендациями (proptype validation и 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') ); } })
в более общем плане, вы также можете конвертировать новые данные в массив и использовать что-то вроде concat:
var newData = this.state.data.concat([data]); this.setState({data: newData})
этот шаблон фактически используется в демо-приложении Todo Facebook (см. раздел "приложение") в https://facebook.github.io/react/.
то, что работало для меня-это преобразование реквизита.данные в массив с помощью
data = Array.from(props.data);
тогда я мог бы использоватьdata.map()
функции
вам не нужен массив, чтобы сделать это.
var ItemNode = this.state.data.map(function(itemData) { return ( <ComponentName title={itemData.title} key={itemData.id} number={itemData.id}/> ); });
это происходит потому, что компонент отображается до того, как асинхронные данные прибыли, вы должны контролировать, прежде чем отрисовывать.
Я решил это таким образом:
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> ); }
- карта не может решить, когда свойство равно null / undefined, поэтому я сначала сделал элемент управления
this.props && this.props.partners.length > 0 ?