Reagieren Sie auf JS - Uncaught TypeError: this.props.data.map ist keine Funktion

109

Ich arbeite mit reactjs und kann diesen Fehler beim Versuch, JSON-Daten (entweder von einer Datei oder vom Server) anzuzeigen, nicht verhindern:

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

Ich habe angeschaut:

Reagieren Sie mit dem Code "TypeError: this.props.data.map ist keine Funktion".

React.js this.props.data.map () ist keine Funktion

Beides hat mir nicht geholfen, das Problem zu beheben. Nachdem meine Seite geladen wurde, kann ich überprüfen, ob this.data.props nicht undefiniert ist (und einen Wert hat, der dem JSON-Objekt entspricht - mit dem aufgerufen werden kann window.foo). Es scheint also, dass es nicht rechtzeitig geladen wird, wenn es von aufgerufen wird Konversationsliste. Wie stelle ich sicher, dass die mapMethode mit den JSON-Daten und nicht mit einer undefinedVariablen arbeitet?

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

BEARBEITEN: Hinzufügen von Beispielkonversationen.json

Hinweis - Der Aufruf gibt this.props.data.conversationsauch einen Fehler zurück:

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

gibt den folgenden Fehler zurück:

Nicht erfasster TypeError: Die Eigenschaft 'map' von undefined kann nicht gelesen werden

Hier ist gespräche.json:

{"user_has_unread_messages":false,"unread_messages_count":0,"conversations":[{"id":18768,"last_message_snippet":"Lorem ipsum","other_user_id":10193}]}
Schlüsselpulsationen
quelle
Ich habe die Antwort aktualisiert. Einige Empfehlungen: Fügen Sie propTypes: {data: React.PropTypes.array} zu ConversationList hinzu, um den Datentyp zu überprüfen, und fügen Sie eine componentWillUnmount: fn () hinzu, die das Intervall in ConversationBox "clearInterval".
user120242

Antworten:

134

Die .mapFunktion ist nur für Arrays verfügbar.
Es sieht so aus, als ob dataes nicht das Format hat, das Sie erwarten (es ist {}, aber Sie erwarten []).

this.setState({data: data});

sollte sein

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

Überprüfen Sie, auf welchen Typ "Daten" gesetzt wird, und stellen Sie sicher, dass es sich um ein Array handelt.

Geänderter Code mit einigen Empfehlungen (propType-Validierung und 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')
    );
  }
})
user120242
quelle
32

Was für mich funktioniert hat, ist die Konvertierung der props.data in ein Array mit, data = Array.from(props.data); dann könnte ich die data.map()Funktion verwenden

Vishal Seshagiri
quelle
1
Mein Typ war bereits ein Array, zB zeigte typeOf das Richtige, auch die Länge war korrekt, aber dennoch trat der Fehler auf. Das hat es für mich behoben. Vielen Dank!
user1829319
10

Im Allgemeinen können Sie die neuen Daten auch in ein Array konvertieren und so etwas wie concat verwenden:

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

Dieses Muster wird tatsächlich in der ToDo-Demo-App von Facebook (siehe Abschnitt "Eine Anwendung") unter https://facebook.github.io/react/ verwendet .

Bresson
quelle
1
Dies ist ein guter Tipp / Trick, aber ich möchte darauf hinweisen, dass dies das eigentliche Problem maskieren und OP nicht lösen würde. Im Falle von OP würde dies nicht helfen, da die Daten immer noch fehlerhaft wären (nicht das, was beabsichtigt war). Das Gespräch würde höchstwahrscheinlich leer enden, da alle Requisiten null wären. Ich würde empfehlen, diesen Tipp nicht zu verwenden, es sei denn, Sie sind sicher, dass Ihre Daten das gewünschte Format haben, damit Sie nicht zu unerwartetem Verhalten führen, wenn die zurückgegebenen Daten nicht Ihren Vorstellungen entsprechen.
user120242
1

Sie benötigen dazu kein Array.

var ItemNode = this.state.data.map(function(itemData) {
return (
   <ComponentName title={itemData.title} key={itemData.id} number={itemData.id}/>
 );
});
CodeGuru
quelle
Wie ist es anders, Bruder?
Amar Pathak
1

Dies geschieht, weil die Komponente vor dem Eintreffen der asynchronen Daten gerendert wird. Sie sollten dies vor dem Rendern steuern.

Ich habe es folgendermaßen gelöst:

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>
    );
}
  • Map kann nicht aufgelöst werden, wenn die Eigenschaft null / undefiniert ist, daher habe ich zuerst ein Steuerelement erstellt

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

JC
quelle
0

Sie müssen das Objekt in ein Array konvertieren, um die mapFunktion verwenden zu können:

const mad = Object.values(this.props.location.state);

Wo this.props.location.stateist das übergebene Objekt in eine andere Komponente.

ISRAEL ODUGUWA
quelle
-2

Versuchen Sie den componentDidMount()Lebenszyklus beim Abrufen von Daten

Arif Ramadhani
quelle
5
Erklären Sie auch, warum und wie dies funktioniert.
Mittal Patel