Cannot read property 'date' of undefined - only when FlatList - javascript

i have a weird problem.
the process:
1) i fetch from api in useEffect an array of object of meetings
useEffect(() => {
trackPromise(
bookingContext.getUserMeetings(state.token, state.userId),
"getUserMeetings"
);
}, []);
2) i render the array conditinally if his render (so first render is empty - and after useEffect is get the array from the "MeetingContext"
{bookingContext.state.userMeetings.length > 0 ? (
<View style={styles.container}>
<Text>{bookingContext.state.userMeetings[0].date}</Text>.............
So far it works
now when i change the last row ( bookingContext.state.userMeetings[0].date} )
to flat list like this:
<FlatList
data={bookingContext.state.userMeetings}
renderItem={({ meet }) => (
<Text>{meet.date}</Text>
)}
keyExtractor={meet => meet._id}
/>
and try to render he tell me "TypeError: Cannot read property 'date' of undefined"
he does not care my conditionnaly or nothing.. its just crush.
4) **i use in react-promise-tracker for the loading and wrap the use effect with it.
the question : why with flat list it doesnt work and in regular syntax(like part 2) it works.
question 2: there is a way to tell react to first wait until the promise done in the use effect and just after he finish to render the array?

Related

React break .map() in condition rendering

How can I use break to break a .map() function when condition rendering is true ?
This is my example code
{messages.length >= 1 ? (
<div>
{messages.reverse().map((e) => (
e.senderName[0] == displayName[0] ? (<p>Status: {e.message}</p>) : null
))}
</div>
) : (<p>Status: Verifying...</p>)}
The problem is in messages array is gonna have a new message add every 60 second and I want to display a new message on my screen that has been add in every 60 second
Then I want to break .map() function after a new message has been add to messages array and display on my screen
What I want is like this enter image description here
But the problem is like this enter image description here
you can't use break to stop .map() method. I see that you are trying to render messages if message belongs to current user.
I can recommend you to filter the messages first, then render the filtered array with mapping. Like:
const usersMessages = messages.reverse().filter((e) => (e.senderName[0] === displayName[0]);
return (
<div>
{usersMessages.length > 0 ? (usersMessages.map(e =>{
return((<p>Status: {e.message}</p>))
})) : <p>Status: Verifying...</p>)}
</div>
)
Of course you must have a part in your code that updates the messages array and triggers a re-render when data is updated.

React - How to iterate over an array of objects and display each item as a component?

I am using React and I have an array of data objects ("items"). I am trying to display this list of objects such that each object is its own component. So I'm calling "items.map" to map each item to a component and render it, like this:
return (
<Fragment>
<h1>Items</h1>
<Card.Group itemsPerRow={8} >
{items.length > 0 ? ( items.map( (_item) => (
<Fragment key={_item.key}>
<MyItem example={4} item={_item} />
</Fragment>
))
) : (<p>No items!</p>)}
</Card.Group>
</Fragment>
)
Here is the code for MyItem:
const MyItem = (example, item) => {
console.log(example);
console.log(item);
return (<Fragment>
<div style={{margin: "1em"}}>
<Card>
<Image src={item.image} wrapped ui={false} />
<Card.Content>
<Card.Header>{item.name}</Card.Header>
<Card.Description>
<p>Date Received: {item.dateReceived.toLocaleDateString()}</p>
</Card.Description>
</Card.Content>
</Card>
</div>
</Fragment>);
}
However, I'm getting an error saying that "item" is null in MyItem.js, while the list of items is definitely not null. Similarly, when I pass in the number 4 to the "example" prop, and I do "console.log(example)" it prints out the empty object "{}".
I should note that my page previously did display everything when both pieces of code were combined on the same page (i.e. there was no "MyItem" component). The reason I decided to make it a component was because I'm iterating over this map on multiple pages and it would be redundant code. This way, I can just call <MyItem item{_item} /> and it will display the same thing on every page that shows these items.
However, now that I have refactored my code to place the mapping inside of the component, it is no longer working. What am I doing wrong here? Is this even possible, or should I just go back to what I had before?
As mentioned in the above comment:
const MyItem = ({example, item})
This solves the problem.

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

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.

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

Categories