React JS - Uncaught TypeError: this.props.data.map is not a function
JavascriptAjaxJsonReactjsJavascript Problem Overview
I'm working with reactjs and cannot seem to prevent this error when trying to display JSON data (either from file or server):
Uncaught TypeError: this.props.data.map is not a function
I've looked at:
https://stackoverflow.com/questions/28434789/react-js-this-props-data-map-is-not-a-function
Neither of these has helped me fix the problem. After my page loads, I can verify that this.data.props is not undefined (and does have a value equivalent to the JSON object - can call with window.foo
), so it seems like it isn't loading in time when it is called by ConversationList. How do I make sure that the map
method is working on the JSON data and not an undefined
variable?
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')
);
}
})
EDIT: adding sample conversations.json
Note - calling this.props.data.conversations
also returns an error:
var conversationNodes = this.props.data.conversations.map...
returns the following error:
> Uncaught TypeError: Cannot read property 'map' of undefined
Here is conversations.json:
{"user_has_unread_messages":false,"unread_messages_count":0,"conversations":[{"id":18768,"last_message_snippet":"Lorem ipsum","other_user_id":10193}]}
Javascript Solutions
Solution 1 - Javascript
The .map
function is only available on array.
It looks like data
isn't in the format you are expecting it to be (it is {} but you are expecting []).
this.setState({data: data});
should be
this.setState({data: data.conversations});
Check what type "data" is being set to, and make sure that it is an array.
Modified code with a few recommendations (propType validation and 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')
);
}
})
Solution 2 - Javascript
You need to create an array out of props.data
, like so:
data = Array.from(props.data);
then will be able to use data.map()
function
Solution 3 - Javascript
More generally, you can also convert the new data into an array and use something like concat:
var newData = this.state.data.concat([data]);
this.setState({data: newData})
This pattern is actually used in Facebook's ToDo demo app (see the section "An Application") at https://facebook.github.io/react/.
Solution 4 - Javascript
It happens because the component is rendered before the async data arrived, you should control before to render.
I resolved it in this way:
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 can not resolve when the property is null/undefined, so I did a control first
this.props && this.props.partners.length > 0 ?
Solution 5 - Javascript
I had the same problem. The solution was to change the useState initial state value from string to array. In App.js, previous useState was
const [favoriteFilms, setFavoriteFilms] = useState('');
I changed it to
const [favoriteFilms, setFavoriteFilms] = useState([]);
and the component that uses those values stopped throwing error with .map function.
Solution 6 - Javascript
Sometimes you just have to check if api call has data returned yet,
{this.props.data && (this.props.data).map(e => /* render data */)}
Solution 7 - Javascript
If you're using react hooks you have to make sure that data
was initialized as an array. Here's is how it must look like:
const[data, setData] = useState([])
Solution 8 - Javascript
You don't need an array to do it.
var ItemNode = this.state.data.map(function(itemData) {
return (
<ComponentName title={itemData.title} key={itemData.id} number={itemData.id}/>
);
});
Solution 9 - Javascript
You need to convert the object into an array to use the map
function:
const mad = Object.values(this.props.location.state);
where this.props.location.state
is the passed object into another component.
Solution 10 - Javascript
As mentioned in the accepted answer, this error is usually caused when the API returns data in a format, say object, instead of in an array.
If no existing answer here cuts it for you, you might want to convert the data you are dealing with into an array with something like:
let madeArr = Object.entries(initialApiResponse)
The resulting madeArr
with will be an array of arrays.
This works fine for me whenever I encounter this error.
Solution 11 - Javascript
I had a similar error, but I was using Redux for state management.
My Error: > Uncaught TypeError: this.props.user.map is not a function
What Fixed My Error:
I wrapped my response data in an array. Therefore, I can then map through the array. Below is my solution.
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
Solution 12 - Javascript
Create an array from props data.
let data = Array.from(props.data)
Then you can use it like this:
{ data.map((itm, index) => {
return (<span key={index}>{itm}</span>)
}}
Solution 13 - Javascript
Add this line.
var conversationNodes =this.props.data.map.length>0 && this.props.data.map(function(conversation, index){.......}
Here we are just checking the length of the array. If the length is more than 0, Then go for it.
Solution 14 - Javascript
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': '2',
I delete that code line in setting it to fix it
Solution 15 - Javascript
You should try this:
const updateNews = async()=>{
const res= await fetch('https://newsapi.org/v2/everything?q=tesla&from=2021-12-30&sortBy=publishedAt&apiKey=3453452345')
const data =await res.json();
setArticles(data)
}
Solution 16 - Javascript
try componentDidMount()
lifecycle when fetching data