TypeError: this.props.messages.map is not a function - javascript

I am using React-Redux and have problem to parse array.
I using selector to return data to component, but I am getting error TypeError: this.props.messages.map is not a function. But if I console.log values it returns me normal array. What I am doing wrong?
Components render method:
render() {
const {loading, error, messages} = this.props;
const mes = JSON.stringify(messages);
console.log(mes); //<------------- returns [{"key":"value"}]
return (
<div>
<MessageList
messages={mes}
/>
);
}
MessageList component map function:
{this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
}
Maybe someone could tell me what I am doing wrong?

What you can do is
render() {
const {loading, error, messages} = this.props;
return (
<div>
<MessageList
messages={messages}
/>
</div>
);
}
Looks like the messages are from redux state. So initially the messages props will be undefined. So before doing map check whether messages is not undefined using conditional operator like below
{this.props.messages && this.props.messages.map((item, i) => (
<MessageItem
key={i}
message={item.message}
/>
))}

In a comment you've said:
messages returns this: List, not array
JavaScript doesn't have a List type, and it's clear from the result of JSON.stringify that messages is an array, or at least something that JSON.stringify thinks is an array.
I think if you look more carefully, you'll find that messages is an array and you can just use it in your MessageList directly.
If messages really, really doesn't have a map method, you can convert it from whatever it is into an array using Array.from:
mes = Array.from(messages);
...or possibly (depending on what it is) spread notation:
mes = [...messages];
You certainly don't want JSON.stringify, which creates a string.

Related

Working with a array of objects in React. Iterating error saying I cannot use object, use array instead

I'm working on a React portfolio project. I have an external data.js file with an array of objects, which I import into the JS file I'm working in. I want to iterate over the array and place each object into it's own div.
I already tried an If Loop and now working with Map(). This is my code:
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'><ProjectItem /></h1>
</div>
)
}
I don't get the problem of iterating trough an array of objects. This is the error:
Error: Objects are not valid as a React child (found: object with keys {-list of keys-}). If you meant to render a collection of children, use an array instead.
I must overlook a simple thing, but I'm a little stuck at the moment :-)
From the fact you're using project.id (and the error message from React), we can assume that project is an object, which means you're trying to use an object as a React child here:
<div>{project}</div>
The error is that you can't do that. Instead, use properties from the object to come up with how you want it displayed. For instance, if project has a description property:
<div>{project.description}</div>
Or if it has (say) a description and a number of days estimated for the project, you might do:
<div>{project.description} - {project.days} {project.days === 1 ? "day" : "days"}</div>
And so on. The fundamental thing is to provide React with something it can put in the DOM (loosely, "display"), such as strings, numbers, arrays, true (but not false)...
You need to return array from your ProjectItem i.e you need to do this:
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'>{ProjectItem()}</h1>
</div>
)
}
try JSON.stringy(project) like this or you should use your object property.Let's we say project has a property that called name
Here is a working example for you.
const Project = () => {
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{JSON.stringy(project)} || {project.name}</div>
</div>
))
)
return (
<div className='project-kader'>
<h1 className='title'><ProjectItem /></h1>
</div>
)
I hope it can work.
you can use project as object into div element
const ProjectItem = () => (
ProjectenList.map(project => (
<div key={project.id}>
<div>{project.text}</div>
</div>
))
)

Looping through a Map in React

I have a Map object:
let dateJobMap = new Map();
for (let jobInArray of this.state.jobs) {
let deliveryDate: Date = new Date(jobInArray.DeliveryDate);
let deliveryDateString: string = deliveryDate.toLocaleDateString("en-US");
if (dateJobMap.has(deliveryDateString)) {
let jobsForDate: IDeliveryJob[] = dateJobMap.get(deliveryDateString);
jobsForDate.push(jobInArray);
}
else {
let jobsForDate: IDeliveryJob[] = [jobInArray];
dateJobMap.set(deliveryDateString, jobsForDate);
}
}
In my render method, I want to call a TruckJobComp object for each delivery job in the value's array to display it:
<div className={ styles.column }>
<p className={ styles.description }>{escape(this.props.description)}</p>
{
dateJobMap.forEach(function(jobsForDate, dateString) {
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
})
}
</div>
This seems like it should work but doesn't. It never creates a TruckJobComp. I do a .forEach iteration on my Map, and for each value's array, I use .map to get the individual job object to send to TruckJobComp object.
When I create a temp array to grab the jobs from the last loop:
let tempJobs: IDeliveryJob[];
and in the loop add in:
if (dateJobMap.has(deliveryDateString)) {
let jobsForDate: IDeliveryJob[] = dateJobMap.get(deliveryDateString);
jobsForDate.push(jobInArray);
tempJobs = jobsForDate;
}
and then use that array in the render:
<div className={ styles.column }>
<p className={ styles.description }>{escape(this.props.description)}</p>
{
tempJobs.map(job => (
<TruckJobComp job = { job }/>
))
}
</div>
It displays as expected.
I do have a warnings in Visual Studio Code:
Warning - tslint - ...\TruckDeliverySchedule.tsx(104,38): error no-function-expression: Use arrow function instead of function expression
I don't know enough to understand. Line 104 corresponds with:
dateJobMap.forEach(function(jobsForDate, dateString) {
I am very new to this so I'm not 100% sure how most of this works. Just trying to put pieces I've learned together to get things to work.
Second Edit:
{escape(this.props.description)}
{
[...dateJobMap.keys()].map(jobsForDate => // line 154
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
)
}
Produces error:
[09:06:56] Error - typescript - src\...\TruckDeliverySchedule.tsx(154,27): error TS2461: Type 'IterableIterator<any>' is not an array type.
dateJobMap.forEach(...) returns undefined, so it cannot be mapped to a collection of elements.
ES6 maps have forEach method for compatibility purposes (generally for..of is preferred to iterate over iterables) and don't have map method. A map should be converted to array first, then it could be mapped to an element. Since values aren't used, only keys need to be retrieved:
{
[...dateJobMap.keys()].map(jobsForDate =>
jobsForDate.map(job => (
<TruckJobComp job = { job } />
))
)
}
All this warning is saying is that instead of using the syntax function(jobsForDate, dateString) {} you should use the syntax (jobsForDate, dateString) => {}.
The reason could be the way this is scoped in arrow functions versus function expressions. See this post.
My guess as to the reason your first approach didn't work but your second one did is that forEach doesn't actually return an array, and if it did, calling map within forEach would return an array of arrays (but, again, it doesn't). Not sure how React would handle that, but React does know how to handle a single array, which is what your last approach returns.

Unable to render an array of values (React component)

I am building a test app to learn more about React and I have made an API call which gets a huge JSON object.
I was able to break this json into the parts that I need and now I have 10 arrays of 3 props each. I am able to send these 10 arrays in 3 props to another component, which needs to use these 3 props 10 times and render a div class Card each.
I can console.log(this.props) and it shows 10 different arrays with 3 props each,however, I cannot produce a same element 10 times.. I tried using map() but since my array is initially undefined, map() is not able to function properly either. Is there any thing in react like *ngFor in Angular ?
What is the best way to go about this?
*EDIT
Here's more code guys. Sorry still noobie here..
ERROR : this.props.map is not a function
return(
<div>
{this.props.map((data,i)=>{
return(
<li key={i}>{data.likes}</li>
);
*EDIT 2
Soo I tried running map function with an if condition but the code still breaks the very moment the condition gets true..
render() {
if(this.props.url !== undefined){
this.props.map((data,i) =>{
return <li key={i}>{data.likes}</li>
})
}
My state method is :
state = {
userId: undefined,
likes: undefined,
url: undefined
}
and im setting my values on each data stream as follows :
const pics = await fetch(`${base_url}?key=${api_key}&q=${query}
&img_type=photo&per_page=12`).then(response => {
return response.json();
})
pics.hits.map((data) =>{
return this.setState({
userId: data.user_id,
likes: data.likes,
url: data.webformatURL
})
})
this.props won't have map, it's not an array. It's an object with a property for each property passed to your component. For instance:
<YourComponent foo="bar"/>
...will have this.props.foo with the value "bar".
So if you're passing an array to your component, like this:
<YourComponent theArrayProperty={[{likes: 42},{likes:27}]} />
...then you need the name of that property:
return (
<div>
{this.props.theArrayProperty.map((data,i) => {
return (
<li key={i}>{data.likes}</li>
);
})}
</div>
);
Side note: You can use a concise arrow function for the map callback instead:
return (
<div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>
);
...and no need for the () if you put the opening tag on the line with return (you can't leave off the ( if it's on the next line, but you probably knew that):
return <div>
{this.props.theArrayProperty.map((data,i) => <li key={i}>{data.likes}</li>)}
</div>;
...but that's a matter of style.
With little information that you have provided, my guess is that code fails at map() when you try to use it with undefined value.
Try adding a conditional check to render
{props && props.map([RENDER CODE HERE])}
You can just make simple if statement to check if the array is not undefined, and then pass it to map function.
Another option is to set a defaultProps for an empty array.
MyComponent.defaultProps = {
arrProp: []
};

React: props populated with componentWillMount and Redux

So I have this loop in my render:
{this.props.OneTestArrayProp.questions.map((q, i) =>
<div key={i} className="questions_div">
<div>Question: {q.question}</div>
<div>
)}
The this.props.OneTestArrayProp is loaded this way:
componentWillMount() {
if ( ! this.props.OneTestArrayProp.length ) {
this.props.dispatch(fetchOneTest(test_id));
}
}
But I'm getting:
Error: "this.props.OneTestArrayProp.questions.map" undefined
the error appears just for a second and then it disappears when Redux load the data. Of course the root of the problem is that half second when this.props.OneTestArrayProp is populated by the middleware thunk.
There is a way to indicate to React to "wait" before it does the render?
SOLVED
The solution provided by Red Mercury works pretty well, however I coded a more "redux-like" approach adding a new Array:
this.props.QuestionsArrayProp = [];
and linking it with its reducer:
QuestionsArrayProp: state.rootReducer.questions_reducer.QuestionsTestArrayProp
That way I get an initial, empty but existent array.
Check if it exists before mapping over its questions
{this.props.OneTestArrayProp &&
this.props.OneTestArrayProp.questions.map((q, i) =>
<div key={i} className="questions_div">
<div>Question: {q.question}</div>
<div>
)}
If this.props.OneTestArrayProp is undefined the expression will short-circuit and the map will never be executed

How to map a nested array in React.js?

Issue: I can only render one iteration of my array.
My desired result of course is to get the entire length of array objects.
Adding [key] to my rendered object fields is the only method that gives me any output. Without declaring the key in this way, I get nothing
Child Component
...
const Potatoes = ({potatoes}) => {
const PotatoItems = potatoes.map((potato, key) => {
if ([potato] == ''){
return false
} else {
return (
<li key={key}>
<span>{potato[key].name}</span>
<span>{potato[key].flavor}</span>
</li>);
}
});
return (
<div>
<ul>
{PotatoItems}
</ul>
</div>
);
};
Parent Component
...
render () {
const potatoes = new Array(this.props.potatoes);
return (
<section style={divStyle}>
<Potatoes potatoes={potatoes} />
</section>
)
}
Simply removing new Array() from around the potatoes constant fixes your issue.
It seems like you may have created an unnecessary additional array.
Then you can remove those [key] references on your object in the child component and you should be good to go!
Does this fix your issue?

Categories