Cannot read properties of undefined (reading 'data') in React - Comment Section - reactjs

I am implementing a comment section. I am reading the data that is being saved in a database. However when reading the comments from an array of comments its giving issue on this line
const message_list =response.data.results.data.message.map((value) => (value));
Ive taken the code from this
https://riyanegi.github.io/react-comments-documentation/
axios.get( process.env.REACT_APP_API_URL +"product/" + this.product_id +"/idea/"+ this.idea_id +"/comment",{
headers: {
Authorization: "Bearer "+ user_access_token,
}})
.then(response => {
const message_list =response.data.results.data.message.map((value) => (value));
this.setState({ data: [...this.state.data, ...message_list] });
});
And then I am rendering:
render() {
return <CommentSection
currentUser={{
currentUserId: this.currentUser.user,
currentUserImg:
'https://ui-avatars.com/api/name='+this.currentUser.name+ '&background=random',
currentUserFullName: this.currentUser.name
}}
commentData={this.state.data}
onSubmitAction={(data) => this.onSubmitAction(data)}
onEditAction={(data) => this.onEditAction(data)}
onDeleteAction={(data) => this.onDeleteAction(data)}
customNoComment={() => this.customNoComment()}
onReplyAction = {(data)=>this.onReplyAction(data)}
/>
}

Based on the console.log you provided, it appears that response.data.results.data may need to be just response.data. You can confirm this by checking the console output to ensure that message is a property on it, and results isn't.
In any case, if you examine the object in your console to find the message array that you're trying to map, you should be able to find your message array nested somewhere in there.

Related

React fetching data from only one object? Uncaught TypeError: Cannot read properties of null (reading 'map') TMDB API

I am trying to map the data of this api endpoint. It doesn't have a object name like results or something like that. So, what should I use to map that data?
https://api.themoviedb.org/3/movie/10000?api_key=4f298a53e552283bee957836a529baec this is the endpoint that I am trying to use.
this is the state management part, don't mind fetch part btw, I did some arrangements somewhere else to set that movieId;
const [current, setCurrent] = useState([]);
const searchById = (movieId) => {
fetch(API_SEARCH_BY_ID + movieId + API_KEY)
.then((res) => res.json())
.then((data) => {
console.log(data);
setCurrent(data);
console.log(current);
successAlert();
});
};
This is the map function in my component file;
<Container fluid>
{current.map((current, currentReq) => (
<MovieDetails current={current} key={currentReq.id} {...currentReq}></MovieDetails>
))}
</Container>
This is the error I am getting;
Details.js:22 Uncaught TypeError: Cannot read properties of null (reading 'map')
I am still in the learning phase of react, so I need some help here. Probably I am missing something silly here.
Thanks to DanPhilipBejoy, Answer is: generating a empty array named test and pushing the data into that array.
const searchById = (movieId) => {
fetch(API_SEARCH_BY_ID + movieId + API_KEY)
.then((res) => res.json())
.then((data) => {
console.log(data);
const test = [data]
setCurrent(test);
successAlert();
});
};
You don't need a loop if its just one element.
<Container fluid>
<MovieDetails current={current} .../>
</Container>
OR
In case if you want to retain the JSX structure just do
setCurrent([data]);

cant map fetched data in react js (Cannot read properties of undefined (reading 'map'))

I have fetched data from spotify api and it displays in console but when i try to return data inpage it says map is not defined I use useState([]) and pass it to array if I am saying it right way
const [track, setTrack] = useState([])
const getRecommendation = async (e) => {
e.preventDefault()
const {data} = await axios.get("https://api.spotify.com/v1/recommendations", {
headers: {
Authorization: `Bearer ${token}`
},
params: {
limit: '10',
seed_artists: '4NHQUGzhtTLFvgF5SZesLK',
seed_genres: 'rock,pop,metal',
seed_tracks: '0c6xIDDpzE81m2q797ordA'
}
})
setTrack(data.tracks.item)
console.log(data);
}
const renderTracks = () => {
return track.map(tracks => {
return (
<h1 className='track-name'>
{tracks.name}
</h1>
)
})
}
here in console
anu advices? Thanks
According to this:
console.log(data);
The data object has a property called tracks which is an array. Which means this:
setTrack(data.tracks.item);
Is setting the state to undefined, because there is no .item property on an array.
This implies that you're expecting to iterate over an array:
return track.map(tracks => {
So set the state to the array, not some property you expect to be on the array:
setTrack(data.tracks);
As an aside, the plurality in your names is backwards. This is going to cause confusion for you (if it hasn't already). Consider the semantics of this:
return track.map(tracks => {
In this line of code, track is an array and tracks is a single object. That's not how plurality works. Rename your variables to accurately reflect what they contain:
const [tracks, setTracks] = useState([]);
and:
setTracks(data.tracks);
and:
return tracks.map(track => {
This could very well be what caused your problem in the first place, since setTrack implies that it's expecting a single object and data.tracks is an array.
Keep your variable names consistent and meaningful and your code will be much easier to read and understand.

How to make a condition if a list length is plus one?

What I'm trying to do is to set a hook state if in the list is added a new client.
To get the clients I use axios.
useEffect(() => {
const clientParams =
"userName=" +
currentUser
"clientId=" +
currentClient
setClientlist([]);
axios
.get(process.env.REACT_API_URL + clientParams)
.then((response) => {
let newListClient = response.data.map(function (client) {
return {
...client,
id: client.id,
name: client.name,
gender: client.client.gender
};
});
setClientlist(newListClient);
if (newListItem.length + 1 > newListItem.length){
console.log('test')
}
})
.catch((error) => console.log(error));
}, [count]);
So the idea is when a client is saved(which the save function is on another component), the length of the newListClient is added by 1, and since the length is more than the previous one, the condition will be executed.
The problem is that the console.log('test') is shown every time I reload or open the page.
What I want is the console to be shown when the length of the newListItem is added by 1.
How can I make it work?
It is rendering for the first time because on first render everthing is undefined and so undefined + 1 > undefined is true. And when the values are updated by axios it is true as well ( this time with live data ). I thing changing the condition to the following will work.
if ( newListItem && newListItem?.length + 1 > newListItem?.length){
console.log('test')
}

How to replace a loading image in a chat with an image uploaded to Firebase Storage using ReactJS?

I have built a chat using Firebase and ReactJS. I mainly followed their Firebase's web codelab at https://firebase.google.com/codelabs/firebase-web#1. However, I have gotten stuck on the image uploading functionality. Since I am using ReactJS, I have had to modify their plain JS code to match mine. I am able to save a message with a "loading" image url in Firestore, then, I successfully save the image that I want to ultimately show in the chat in Firebase Storage, and finally then I successfully retrieve its url from Storage and replace it with the url of the loading image in Firestore. The image does show in the chat, however, the loading image is not actually replaced but, instead, it remains in the chat when I want it to be completely replaced, obviously, so that the loading image is no longer there. Here's what I mean, in this image:
As you can see the loading image on top stayed on instead of being replaced by the image underneath it. I think it should be filtered out somehow before I save the new snapshot with the new image url. However, I can not figure out how to do it correctly. I tried to filter it out based on the url of the loading image which is saved locally but since it is saved as a base64 in Storage, it did not work. Neither did using the actual Base64 code as a way to filter it out. So, I need help to solve this issue. The codelab does not really specify this nor is it clear how they do it in their code which is in plain Javascript anyways and I use ReactJS so it may not be 100% suitable.
Here's, I believe, enough code to see what is going on. Let me know if you need more of it.
Here's how I send images to the Chat: (modeled on the Firebase codelab)
sendImageToChat () {
this.state.chatFiles.forEach((file) => {
firebase.firestore().collection('Chats')
.doc(this.state.uid)
.collection('Messages')
.add({
docId: this.state.docId,
imageUrl: loadingLogo,
timestamp: new Date(),
uid: this.state.uid,
name: this.state.displayName,
email: this.state.email
})
.catch((error) => {
this.setState({ writeError: error.message });
})
.then((messageRef) => {
// 2 - Upload the image to Cloud Storage.
const filePath = `users/${this.state.displayName}/${this.state.uid}/${moment().format("MMM Do YY")}/${uuidv4()}/${file.name}`
return firebase.storage().ref(filePath).put(file).then((fileSnapshot) => {
// 3 - Generate a public URL for the file.
return fileSnapshot.ref.getDownloadURL().then((url) => {
// 4 - Update the chat message placeholder with the image's URL.
return messageRef.update({
imageUrl: url,
storageUri: fileSnapshot.metadata.fullPath
});
});
});
}).catch(function(error) {
console.error('There was an error uploading a file to Cloud Storage:', error);
});
})
this.setState({
chatFiles: []
})
document.getElementById('file-1').value = "";
}
Here's how I, then, setState when the loading image is added and then when its url is modified: (Notice how I try to filter out the loadingLogo which is the loading image out of the state but it does not obviously work for the reason explained above).
startChat () {
document.getElementById("myForm").style.display = "block";
const ref = firebase.firestore().collection('Chats').doc(this.state.uid).collection('Messages');
const query = ref.orderBy('timestamp', 'desc').limit(10)
this.unsubFromMessages = query.onSnapshot((snapshot) => {
if (snapshot.empty) {
console.log('No matching documents.');
firebase.firestore().collection('Chats').doc(this.state.uid).
set({
name: this.state.displayName,
uid: this.state.uid,
email: this.state.email
}).then(console.log("info saved"))
.catch((error) => {
console.log("Error saving info to document: ", error);
});
}
snapshot.docChanges().reverse().forEach((change) => {
if (change.type === 'removed') {
console.log(change.doc.data().content)
} else if (change.type === 'added') {
this.setState(state => {
const messages = [...state.messages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
} else if (change.type === 'modified') {
const filteredMessages = this.state.messages.filter(message => message.imageUrl !== loadingLogo)
console.log(filteredMessages)
this.setState(state => {
const messages = [...filteredMessages, {id: change.doc.id, body: change.doc.data()}]
return {
messages
}
})
setTimeout( this.scrollToBottom(), 2000)
}
});
}, (error) => {console.log(error)});
}
This is part of the Chat's JSX:
<div className="chatArea" id='messages'>
{
this.state.messages.map((message, index) => {
return message.body.uid === this.state.uid
?
<div>
{
message.body.imageUrl ?
<img src={message.body.imageUrl} className="message-sent"></img>
:
<p className="message-sent" key={index}>{message.body.content}</p>
}
</div>
:
<p className="message-received" key={index}>{message.body.content}</p>
})
}
<div style={{ float:"left", clear: "both" }}
ref={(el) => { this.myRef = el; }}>
</div>
</div>
I know the issue is not with Firebase but rather with ReactJS. I know I need to remove, filter out, replace or delete that loading image before or after the modified message with the new url is saved to the state. So, please help me figure this out. I am sure many people may encounter this problem.
Thank you!
I figured it out. I might as well delete this question but it may help someone build a chat with ReactJS and Firebase. Anyways, my approach to filter out based on the object property, imageUrl is a viable option. It works! My silly oversight was that I did not add the parent property or object, "body", after the object "message". More specifically, instead of const filteredMessages = this.state.messages.filter(message => message.imageUrl !== loadingLogo), it should be const filteredMessages = this.state.messages.filter(message => message.body.imageUrl !== loadingLogo). You can also try to add an object property that you can use to filter out messages with, for example, allowed: yes or no. If you need more clarification, just ask me, I am glad to help. Happy coding!

MapMarker does not update description onPress immediately

I am trying to learn how to use React Native maps and I am trying to add in custom Map Markers. For some reason, when I try to use the following code, the image updates properly but the description does not update properly until the second click. The first click will show "not selected" but clicking on the same marker will show the actual text I want. How can I fix this?
Since the image is updating to the newImage, I know
this.state.selectedMarkerIndex === i
but the same equality does not apply to description for some reason?
state = {busText:[]}
fetchData=(i, index)=>{
fetch('LINK TO GET BUSES'+ i.toString() + '/buses', {method:'GET'})
.then((response) => response.json())
.then((responseJson) => {
this.setState({
selectedMarkerIndex: index,
busText: responseJson
},
()=>{
console.log("selected index: " + this.state.selectedMarkerIndex)
}
)
console.log(JSON.stringify(this.state.busText));
console.log("_______________________________________________________");
})
}
renderMarkers = ()=>{
return busStops.stops.map((stop, i) => {
return <Marker marker
key={ `${i}` }
coordinate={{latitude: stop.location.latitude, longitude: stop.location.longitude}}
title = {stop.stopName}
image = {this.state.selectedMarkerIndex === i ? newImage : busStopImage}
description={this.state.selectedMarkerIndex === i ? JSON.stringify(this.state.busText) : "not selected"}
onPress={() => {
this.fetchData(stop.stopID, i)
console.log(this.state.selectedMarkerIndex + "i :" + i)
}
}
/>
})
}
I expect the description of the MapMarker to be updated when I click on it with what is fetched but that is not occurring.
A couple of things: To guarantee execution after setState you'll need to put your
this.setState({busText: responseJson})
in the fetchData() callback. Even better, set busText state earlier, where you're setting selectedMarkerIndex state.
Also for what it's worth, if you're having response time issues, try dropping some of your console.log() calls. Native (especially iOS) gets bogged down by them.

Resources