React JS - Uncaught TypeError: this.props.data.map không phải là một hàm


110

Tôi đang làm việc với reactjs và dường như không thể ngăn được lỗi này khi cố gắng hiển thị dữ liệu JSON (từ tệp hoặc máy chủ):

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

Tôi đã xem:

Ném mã phản ứng “TypeError: this.props.data.map không phải là một hàm”

React.js this.props.data.map () không phải là một hàm

Cả hai điều này đều không giúp tôi khắc phục sự cố. Sau khi trang của tôi tải, tôi có thể xác minh rằng this.data.props không phải là không xác định (và có giá trị tương đương với đối tượng JSON - có thể gọi bằng window.foo), vì vậy có vẻ như nó không tải đúng lúc khi nó được gọi bằng ConversationList. Làm cách nào để đảm bảo rằng mapphương thức đang hoạt động trên dữ liệu JSON chứ không phải một undefinedbiến?

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

CHỈNH SỬA: thêm các cuộc hội thoại mẫu.json

Lưu ý - việc gọi this.props.data.conversationscũng trả về lỗi:

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

trả về lỗi sau:

Uncaught TypeError: Không thể đọc thuộc tính 'bản đồ' của undefined

Đây là cuộc trò chuyện.json:

{"user_has_unread_messages":false,"unread_messages_count":0,"conversations":[{"id":18768,"last_message_snippet":"Lorem ipsum","other_user_id":10193}]}

Tôi đã cập nhật câu trả lời. Ngoài ra, một số đề xuất: thêm propTypes: {data: React.PropTypes.array} vào ConversationList để xác minh loại dữ liệu và thêm một componentWillUnmount: fn () "clearInterval" khoảng thời gian trong ConversationBox.
user120242

Câu trả lời:


134

Các .mapchức năng chỉ có trên mảng.
Có vẻ như datanó không ở định dạng mà bạn mong đợi (nó là {} nhưng bạn đang mong đợi []).

this.setState({data: data});

nên là

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

Kiểm tra loại "dữ liệu" đang được đặt thành và đảm bảo rằng nó là một mảng.

Mã đã sửa đổi với một số khuyến nghị (xác thực propType và 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')
    );
  }
})

32

những gì làm việc cho tôi là chuyển đổi props.data thành một mảng bằng cách sử dụng data = Array.from(props.data); thì tôi có thể sử dụng data.map()hàm


1
Kiểu của tôi đã là một mảng, ví dụ typeOf đã hiển thị đúng thứ cũng như độ dài đúng nhưng vẫn còn, lỗi xuất hiện. CÁi này đã sửa nó giúp tôi. Cảm ơn!
user1829319

10

Nói chung, bạn cũng có thể chuyển đổi dữ liệu mới thành một mảng và sử dụng một cái gì đó như concat:

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

Mẫu này thực sự được sử dụng trong ứng dụng demo ToDo của Facebook (xem phần "Ứng dụng") tại https://facebook.github.io/react/ .


1
Đây là một mẹo / thủ thuật nhỏ gọn, nhưng tôi muốn lưu ý rằng điều này sẽ che dấu vấn đề thực sự và sẽ không giải quyết được OP. Trong trường hợp OP, điều này sẽ không giúp ích được gì, vì dữ liệu sẽ vẫn không đúng định dạng (không phải như dự định). Cuộc hội thoại rất có thể sẽ kết thúc trống vì tất cả các đạo cụ sẽ trống. Tôi khuyên bạn không nên sử dụng mẹo này, trừ khi bạn chắc chắn dữ liệu của mình ở định dạng bạn muốn, vì vậy, bạn sẽ không gặp phải hành vi không mong muốn khi dữ liệu trả về không như bạn nghĩ.
user120242

1

Bạn không cần một mảng để làm điều đó.

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

Nó khác nhau như thế nào hả anh?
Amar Pathak

1

Điều này xảy ra vì thành phần được hiển thị trước khi dữ liệu không đồng bộ đến, bạn nên kiểm soát trước khi kết xuất.

Tôi đã giải quyết nó theo cách này:

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>
    );
}
  • Bản đồ không thể giải quyết khi thuộc tính null / không xác định, vì vậy tôi đã thực hiện một điều khiển trước

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


0

Bạn cần chuyển đổi đối tượng thành một mảng để sử dụng maphàm:

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

đâu this.props.location.statelà đối tượng được truyền vào thành phần khác.


-2

thử componentDidMount()vòng đời khi tìm nạp dữ liệu


5
Ngoài ra, cung cấp một số giải thích, tại sao điều này hoạt động và làm thế nào?
Mittal Patel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.