Cannot access key values in an object with React - javascript

Working on a project in React using a MongoDB database. I am trying to access key values in objects, but I get an error when I try to access them. I can access the objects themselves, but as soon as I use dot notation, React crashes and says TypeError: Cannot read property 'title' of undefined. The only instance I can get the data is when I console log in my fetchData() function before it goes through componentDidMount.
class TimelineContainer extends Component {
constructor(props){
super(props)
this.state = {
lifeEvents: [],
currentUser: auth.currentUser
}
}
componentDidMount(){
this.fetchData()
}
fetchData(){
LifeEventModel.all().then( (res) => {
this.setState ({
lifeEvents: res.lifeEvents,
uid: res.lifeEvents.uid,
currentUser: auth.currentUser
})
console.log(this.state.lifeEvents[0].title)
})
}
Link to database backend hosted on heroku. Link to my github repo.
render(){
console.log(this.state.lifeEvents[0].title)
console.log(this.state.lifeEvents);
return (
<div className='timelineContainer'>
{
(this.state.currentUser != null) ?
<div>
<CreateLifeEventForm
currentUser= {this.state.currentUser}
onCreateLifeEvent={this.createLifeEvent.bind(this)} />
<Timeline
currentUser= {this.state.currentUser}
lifeEvents={this.state.lifeEvents}
onDeleteLifeEvent={this.deleteLifeEvent.bind(this)}
onUpdateLifeEvent={this.updateLifeEvent.bind(this)}
/>
</div> :
<section className="col-md-4 col-sm-12 add-event">Log in to add a life event</section>
}
</div>
)
}
}
render method above. First console log throws the error. Second one does not.

it is possible that since you are not fetching your data asynchronously, the initial result this.state.lifeEvents will be null. so you have to first check for the null value until react updates the state and re-renders.
try this out:
renderView = () => {
return (this.state.lifeEvents === null)? <div>Loading...</div> :
(<div>
<CreateLifeEventForm
currentUser= {this.state.currentUser}
onCreateLifeEvent={this.createLifeEvent.bind(this)} />
<Timeline
currentUser= {this.state.currentUser}
lifeEvents={this.state.lifeEvents}
onDeleteLifeEvent={this.deleteLifeEvent.bind(this)}
onUpdateLifeEvent={this.updateLifeEvent.bind(this)}
/>
</div>)
};
render(){
return (
<div className='timelineContainer'>
{
(this.state.currentUser != null) ? this.renderView() :
<section className="col-md-4 col-sm-12 add-event">Log in to add a life event</section>
}
</div>
)
}

Related

Why isn't this React iteration of images working?

I'm learning React and calling Dog API. I got it to work for rendering an image, but when I tried to render multiple images, it doesn't work. For context, the API call link is https://dog.ceo/api/breeds/image/random/5 where "/5" specifies the number of images. I'm pretty sure that the state is being set because when I put console.log instead of setting the state, it's giving me the JSON with the urls to the images. I'm getting back an error message of "Cannot read property 'map' of undefined".
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
}
}
componentDidMount() {
fetch("https://dog.ceo/api/breeds/image/random/5")
.then(response => response.json())
.then(json => this.setState({data: json}));
}
render() {
return (
<div>
{this.state.data.message.map((item, id) => (
<img src={item} key={id} alt="dog" />
))}
</div>
)
}
}
export default App;
this.state.data.message doesn't exist when the component first loads. Instead, you've set this.state.data to an empty array, then later replace it with an object. It's best to keep the types consistent.
The simplest change is probably just to set up this.state.data.message to be an empty array:
constructor(props) {
super(props);
this.state = {
data: {
message: []
},
}
}
From there, taking note of the asynchronous nature of the AJAX operation, you might also consider implementing a "loading state" for when there are no images to display. (Perhaps even a meaningful empty state for when the operation has completed and there are still no images? An error state? etc.)
Check if the data has been manipulated or not. If not yet set the state by the API call then there is nothing this.state.data.message.
Note that, the ?. sign is called Optional Chaining. Optional chaining is used to check if an object has it's key or not in the deep level & also the Optional Chaining does not have the IE support.
render() {
return (
<div>
{this.state.data?.message?.length > 0 && this.state.data.message.map((item, id) => (
<img src={item} key={id} alt="dog" />
))}
</div>
);
}
It happened because when the page initially rendered, the data state is just an empty array. You have to add ? as an optional chaining which basically 'ignore' the error when you are accessing a property of something undefined.
Your code should be something like this
render() {
return (
<div>
{this.state.data.message?.map((item, id) => (
<img src={item} key={id} alt="dog" />
))}
</div>
)
}
So when the data state is empty it would not render anything.
Another way to do it is to check whether the state has content with
render() {
return (
<div>
{this.state.data.message &&
this.state.data.map((item, id) => (
<img src={item} key={id} alt="dog" />
))}
</div>
)
This code will check whether the data.message is truthy and will render the component, otherwise it will render nothing. More info here
}

Some data in an API call is returning Error in a react project

Please I don't know the reason why I'm getting '"URL of undefined" when accessing data in an API call? Presently, I'm working with an API in a react project and I needed to access an image URL in the data, Every other data I'm accessing works perfectly except the image URL.
codeSaandbox
Here is my working code except for the Image URL.
import React, { Component } from "react";
import axios from "axios";
export default class Cat extends Component {
state = {
data: [],
CatWeight: "",
CatMetric: ""
};
componentDidMount() {
this.fetchCountryData();
}
fetchCountryData = async () => {
const url = "https://api.thecatapi.com/v1/breeds";
try {
const response = await axios.get(url);
const data = await response.data;
this.setState({
data
});
const [CatWeight, CatMetric] = this.statistics();
this.setState({
CatWeight,
CatMetric
});
} catch (error) {
console.log(error);
}
};
render() {
return (
<div>
<h2>Cat Assignmaent</h2>
{console.log(this.state.data)}
{this.state.data.map((item, id) => {
return (
<div key={id}>
{/* <img src={item.image.url} alt=""/> Please why is this giving url undefined ? */}
<h2> {item.name}</h2>
<h3> {item.origin}</h3>
<h2>{item.temperament}</h2>
<h2>{item.life_span} kg</h2>
<p>{item.description}</p>
</div>
);
})}
</div>
);
}
}
simply because you are trying to access URL property at the moment your data does not exist yet. and as you know Javascript is Synchronous,
you could fix it by using Optional chaining
<img src={item?.image?.url} alt=""/>
check this
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
While the explanation in the answer of #Swarup is correct, I would rather use this:
render() {
return (
<div>
<h2>Cat Assignmaent</h2>
{this.state.data.map((item, id) => {
return (
<div key={id}>
{
item.image
? <img src={item.image.url} alt={item.name} />
: 'No image, sorry'
}
<h2>{item.name}</h2>
<h3>{item.origin}</h3>
<h2>{item.temperament}</h2>
<h2>{item.life_span} kg</h2>
<p>{item.description}</p>
</div>
);
})}
</div>
);
}
Seems like this is an issue with the API. Some records don't image key in them. To verify this, add following lines after receiving response.
const filtered = data.filter(item => !item.image);
console.log(filtered);
To avoid this you can add a check before displaying an image.
{item?.image?.url && <><img src={item.image.url} alt=""/></> }
You are trying to access state data which is not exist yet, You must have initial state data that contains default URL for image, until you get image data from the API response, so by that you can avoid this problem,
and one thing i may suggest you need to have type safety for the response, so that you would know already what type of data you are going to get from the API response.

ReactJs: Mulitiple Axios calls in componentDidMount()

I want to fetch data from my API (Spring Boot) to my front-end app on React, everything is working fine on the server-side.
Now I have a component named HomePage, where I want to fetch the array of Articles from my API. The problem is, somehow I am getting multiple responses from which the first and second ones are empty arrays, and the third one is the normal array with the needed data.
constructor(props){
super(props);
this.state = {
articles: []
}
}
componentDidMount(){
this.loadArticles();
}
loadArticles(){
articleService.fetchArticles().then((response) => {
this.setState({
articles: response.data
})
});
}
By inspecting with the React DevTool extension, I can see that the state has successfully changed and yet my component doesn't re-render. (If I don't check if articles are empty I get an error which says the .map() is undefined, which means that when the component was rendered it state.articles was filled with the initial response which is always empty)
render() {
let { articles } = this.state.articles;
return (
<div className="container">
<div className="row">
<div className="col-md-9">
{articles && articles.map(article => <Article key={article.id} article={article}/>)}
</div>
<div className="col-md-3">
widgets
</div>
</div>
</div>
)
}
let { articles } = this.state;
not let {articles} = this.state.articles

Getting a Objects are not valid as a React child error even though I am not trying to render an object

As the title states, I am making an api request, and then returning a component with the contents of the api request as the prop. However, idk why I keep getting this error.
App.js
showPicture = async () =>{
//console.log(KEY)
const response = await picOfTheDay.get('',{
params: {
date:this.date,
hd: true,
api_key: 'KEY'
}
});
//console.log()
this.setState({picDayFullDescription: response}, ()=>{
return <PictureOfTheDay date = {this.state.date} picture= {this.state.picDayFullDescription.url} description={this.state.picDayFullDescription.explanation} />
})
}
render(){
return(
<div>
{/* <PictureOfTheDay date = {this.state.date} picture= {this.state.picDayFullDescription.url} description={this.state.picDayFullDescription.explanation}/> */}
{this.showPicture()}
</div>
)
}
PictureOfTheDay.js
class PictureOfTheDay extends React.Component{
constructor(props){
super(props);
}
render(){
return(
<div>
Hello?
</div>
)
}
}
Can someone please point me to the right direction
Instead of calling the function in the render, I would rather put the component on the render and then call the fetch function on some lifecycle hook like componentDidMount.
This updates the state, hence re-rendering the component and the PictureOfTheDay... If the component does not work with an empty description etc which might be a cause of you wanting to make sure the fields are there, render it conditionally based on the needed information e.g {this.state.picDayFullDescription && ...}
// App.js
componentDidMount() {
this.showPicture();
}
showPicture = async () => {
const response = await picOfTheDay.get("", {
params: {
date: this.date,
hd: true,
api_key: "KEY",
},
});
this.setState({ picDayFullDescription: response });
}
render() {
return (
<div>
<PictureOfTheDay
date={this.state.date}
picture={this.state.picDayFullDescription.url}
description={this.state.picDayFullDescription.explanation}
/>
</div>
);
}

React : How to Show Message if there is no records

I am working on project in ReactJS, I am fetching data from server through API. I did some search filtration, I want to display message if there is no records available? I am beginner to ReactJS and don't have much knowledge related to ReactJS.
Code:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
Item: 5,
skip: 0
}
this.handleClick = this.handleClick.bind(this);
}
urlParams() {
return `http://localhost:3001/meetups?filter[limit]=${(this.state.Item)}&&filter[skip]=${this.state.skip}`
}
handleClick() {
this.setState({skip: this.state.skip + 1})
}
render() {
return (
<div>
<a href={this.urlParams()}>Example link</a>
<pre>{this.urlParams()}</pre>
<button onClick={this.handleClick}>Change link</button>
</div>
)
}
}
ReactDOM.render(<Example/>, document.querySelector('div#my-example' ))
You can check when you get the data back and set an error if no data:
getData(){
const {Item,skip}=this.state;
axios.get(`http://localhost:8001/parties?filter[limit]=${Item}&&filter[skip]=${skip}`)
.then(response=>{
console.log(response.data);
if (!response.data.length) {
this.setState({noData: true})
} else {
this.setState({
data:response.data, noData: false
})
}
})
}
Then in your render function:
render() {
if (this.state.noData) {
return <p>No Data was returned!</p>;
}
...
You could check for the data before render the component and return another component if you don't have any. Using expressions such This example ‘’’ { doIHaveData ? < Component1 /> : < Component2 />} ‘’’
Where Component1 has your functionality and Component2 return a message or a spinner loader whatever you want .
I hope it helps!
When you are checking for the empty array you could check for array type and length. I would personally do something like
{Array.isArray(array) && array.length === 0 ? <component1/> : <component2/>}
You can make use of conditional rendering!
render(){
const filteredItems = this.getDataItems(this.state.filtered);
const dataItems = this.getDataItems(this.state.data);
if(dataItems){
return(
<div>Your Message</div>
)
}
else{
//Your normal code
}
}
I think a nice way would be to make your back end return a error message when there are no records to display.
You could use .catch after your axios.get().then() function to see if there are any errors returned by back end and then display it to the user.
Look: What is the proper REST response code for a valid request but an empty data?
You can add your message as another conditional wherever you'd like:
{this.state.filtered.length === 0 && (
<div>Your Message</div>
)}
Or if you're trying to add a message for getting no results at all, then:
{this.state.allData.length === 0 && (
<div>Your Message</div>
)}

Categories

Resources