React JS - Uncaught TypeError:this.props.data.map不是函数

时间:2015-05-09 16:24:57

标签: javascript ajax json reactjs

我正在使用reactjs,并且在尝试显示json数据时(无论是来自文件还是服务器)似乎无法阻止此错误:

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:

{&#34; user_has_unread_messages&#34;:假,&#34; unread_messages_count&#34;:0,&#34;会话&#34;:[{&#34; ID&#34;:18768&# 34; last_message_snippet&#34;:&#34; Lorem ipsum&#34;,&#34; other_user_id&#34;:10193}]}

12 个答案:

答案 0 :(得分:90)

.map功能仅适用于阵列 看起来data不是您期望的格式(它是{},但您期望[])。

this.setState({data: data});

应该是

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

检查什么类型&#34;数据&#34;被设置为,并确保它是一个数组。

带有一些建议的修改代码(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')
    );
  }
})

答案 1 :(得分:13)

对我有用的是将props.data转换为使用的数组 data = Array.from(props.data); 然后我可以使用data.map()函数

答案 2 :(得分:8)

更一般地说,您还可以将新数据转换为数组并使用concat:

之类的内容
var newData = this.state.data.concat([data]);  
this.setState({data: newData})

这种模式实际上用于Facebook的ToDo演示应用程序(请参阅“#34;应用程序&#34;”部分)https://facebook.github.io/react/

答案 3 :(得分:2)

我遇到了同样的问题。解决方案是将 useState 初始状态值从字符串更改为数组。 在 App.js 中,之前的 useState 是

const [favoriteFilms, setFavoriteFilms] = useState('');

我改成

const [favoriteFilms, setFavoriteFilms] = useState([]);

并且使用这些值的组件停止使用 .map 函数抛出错误。

答案 4 :(得分:1)

您不需要数组来执行此操作。

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

答案 5 :(得分:1)

这是因为组件在异步数据到达之前呈现,您应该在呈现之前进行控制。

我用这种方式解决了这个问题:

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时,Map无法解析,所以我先做了一个控件

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

答案 6 :(得分:1)

您需要将对象转换成数组才能使用map函数:

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

其中this.props.location.state是传递给另一个组件的对象。

答案 7 :(得分:1)

如果您使用的是 React 钩子,您必须确保 data 被初始化为一个数组。下面是它的样子:

const[data, setData] = useState([])

答案 8 :(得分:1)

有时你只需要检查 api 调用是否有返回数据,

{this.props.data && (this.props.data).map(e => /* render data */)}

答案 9 :(得分:0)

在获取数据时尝试componentDidMount()生命周期

答案 10 :(得分:0)

如已接受的答案中所述,此错误通常是由于API以某种格式(例如对象)而非数组形式返回数据。

如果这里没有现成的答案适合您,您可能希望将要处理的数据转换为数组,例如:

let madeArr = Object.entries(initialApiResponse)

产生的madeArr将是一个数组数组。

当我遇到此错误时,这对我来说很好。

答案 11 :(得分:-1)

我有一个类似的错误,但是我正在使用Redux进行状态管理。

我的错误:

未捕获的TypeError:this.props.user.map不是函数

修复我的错误的原因:

我将响应数据包装在一个数组中。因此,我可以映射整个数组。下面是我的解决方案。

const ProfileAction = () => dispatch => {
dispatch({type: STARTFETCHING})
AxiosWithAuth()
    .get(`http://localhost:3333/api/users/${id here}`)
    .then((res) => {
        // wrapping res.data in an array like below is what solved the error 
        dispatch({type: FETCHEDPROFILE, payload: [res.data]})
    }) .catch((error) => {
        dispatch({type: FAILDFETCH, error: error})
    })
 }

 export default ProfileAction