the screenshots attached are the result of an API fetch (movie database).
From this results, as shown in the screenshots i am trying to reach for the "primaryImage.url" yet I keep on getting "Cannot read properties of undefined (reading 'url').
Here is my page:
const SearchPage = () => {
const params = useParams();
const { query } = params;
const [data, setData] = useState(null);
const fetchData = () => {
fetch(`${rapidApiUrl}/search/title/${query}${rapidSearchData}`, rapidApiOptions)
.then((response) => response.json())
.then((data) => {
setData(data.results);
})
.catch((err) => console.error(err));
};
useEffect(() => {
fetchData();
}, [query]);
console.log(data)
return (
<div>
{data &&
data.map((media) => (
<div key={media.id}>
{/* <img src={media.primaryImage.url} alt={media.titleText.text} /> */}
<p>{media.titleText.text}</p>
<p>{media.releaseYear.year}</p>
</div>
))}
</div>
);
};
export default SearchPage;
As you are looping through the data, some of the primaryImage values are objects, which do indeed have a url property, but some of them are null. When attempting to find the url property of null, it errors out. You probably want to check to see if primaryImage is null and handle it differently than if it has a value.
The problem is primaryImage property appears to be null for few cases. You should use optional chaining to avoid the errors.
return (
<div>
{data &&
data.map((media) => (
<div key={media.id}>
<img src={media.primaryImage?.url} alt={media.titleText?.text} /> */}
<p>{media.titleText.text}</p>
<p>{media.releaseYear.year}</p>
</div>
))}
</div>
);
You can read more about optional chaining here
Related
I am trying to render text from an API call, text or numbers that are directly accesible from the axios.data object can render normally, nevertheless when inside the axios.data there is another object with its own properties I cannot render because an error shows, the error is 'undefined is not an object (evaluating 'coin.description.en')', over there description is an object; my code is
function SINGLE_COIN(props) {
const { id } = useParams()
console.log(id);
const SINGLE_API = `https://api.coingecko.com/api/v3/coins/${id}?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true`
const [coin, setCoin] = useState({})
useEffect(() => {
axios
.get(SINGLE_API)
.then(res => {
setCoin(res.data)
console.log(res.data)
})
.catch(error => {
console.log(error)
})
}, [])
return (
<div>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
<SINGLE_COIN_DATA coin={coin} />
</div>
)
}
Thanks!
For the initial render (data is not fetched yet), it will be empty. so nested property would be undefined.
so note the changes:
Example 1:
const [coin, setCoin] = useState(null);
..
return (
<div>
{coin ? (
<>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
</>
) : null}
</div>
);
Example:2: Use the optional chaining while accessing nested property
return (
<div>
<h2>{coin?.name}</h2>
<div>{coin?.coingecko_score}</div>
<div>{coin?.liquidity_score}</div>
<div>{coin?.description?.en}</div>
</div>
);
And the complete code with : working example
export default function SINGLE_COIN() {
const { id } = useParams()
const SINGLE_API = `https://api.coingecko.com/api/v3/coins/${id}?tickers=true&market_data=true&community_data=true&developer_data=true&sparkline=true`;
const [coin, setCoin] = useState(null);
useEffect(() => {
axios
.get(SINGLE_API)
.then((res) => {
setCoin(res.data);
})
.catch((error) => {
console.log(error);
});
}, []);
return (
<div>
{coin ? (
<>
<h2>{coin.name}</h2>
<div>{coin.coingecko_score}</div>
<div>{coin.liquidity_score}</div>
<div>{coin.description.en}</div>
</>
) : null}
</div>
);
}
I don't know how to make multiple searches while I'm doing my assignment.
I'm trying a lot of things. If anyone knows, please answer.
app.jsx
const [touristSpot, setTouristSpot] = useState([]);
const [filterTourlistSpot, setFilterTourlistSpot] = useState([]);
const [searchItem, setSearchItem] = useState("");
const filedConfig = {
method: "get",
url: "http://localhost:3000/fields",
};
const recordConfig = {
method: "get",
url: "http://localhost:3000/records",
};
useEffect(
() =>
axios(filedConfig)
.then((res) => console.log(res?.data))
.catch((err) => console.log(err)),
[]
);
useEffect(
() =>
axios(recordConfig)
.then((res) => setTouristSpot(res?.data))
.catch((err) => console.log(err)),
[]
);
console.log(searchItem);
useEffect(() => {
setFilterTourlistSpot(() =>
// touristSpot?.filter((spot) => spot.관광지명.includes(searchItem)) //first try
// touristSpot?.filter((spot) => new RegExp(searchItem).test(spot.관광지명))//second try
touristSpot?.filter((spot) => spot.관광지명.match(searchItem))//final try
);
}, [searchItem, touristSpot]);
return (
<>
<Header />
<Search
touristSpot={touristSpot}
setTouristSpot={setTouristSpot}
filterTouristSpot={filterTourlistSpot}
setFilterTouristSpot={setFilterTourlistSpot}
searchItem={searchItem}
setSearchItem={setSearchItem}
/>
<SpotList filterTourlistSpot={filterTourlistSpot} />
</>
);
spotlist.jsx
const Spotlist = ({ filterTourlistSpot }) => {
return (
<div className={styles.container}>
{filterTourlistSpot.map((spot) => (
<SearchSpot spot={spot} />
))}
</div>
);
};
searchspot.jsx
const Searchspot = ({ spot }) => {
const onClick = (e) => {
console.log(spot);
};
return (
<div className={styles.spotbox} onClick={onClick}>
<h3>{spot.관광지명}</h3>
</div>
);
};
What I want is to show you the search results that fit the keyword when you enter more than one keyword.
Together in the situation below.
Your details are a bit unclear, you should provide the errors you are getting, if any, and if possible a translation of the words that are not in english, since they seem to be used as variables and/or props.
If your error happens during the mapping in spotlist.jsx and everything else is working, then try this:
spotlist.jsx
const Spotlist = ({ filterTourlistSpot }) => {
return (
<div className={styles.container}>
{filterTourlistSpot().map((spot) => (
<SearchSpot spot={spot} />
))}
</div>
);
};
You forgot to call your filterTourlistSpot function before mapping the array returned from the filtering inside the body of that function.
If this does not solve your issue, please provide some more information about your problem and I will edit my answer to give a better solution.
I'm parsing data from the NASA API using React, and for some reason I can map one nested object within the return but not the other.
Here is my parent component:
import React, { useState } from 'react'
import './NasaAPI.scss'
import NasaImages from './NasaImages'
const NasaAPI = () => {
const [nasaData, setNasaData] = useState([])
const [nasaImage, setNasaImage] = useState("")
const [searchInput, setSearchInput] = useState("")
const [loading, setLoading] = useState(true)
const fetchData = async (e) => {
const data = await fetch(`https://images-api.nasa.gov/search?q=${searchInput}`)
.then(response => response.json())
.then(data => setNasaData(data.collection.items))
.catch(err => console.log(err))
.finally(setLoading(false))
}
const handleSubmit = (e) => {
e.preventDefault()
fetchData()
}
const handleChange = (e) => {
setSearchInput(e.target.value)
}
return (
<div>
<h2>Search NASA Images</h2>
<form onSubmit={handleSubmit}>
<input name="searchValue" type="text" value={searchInput} onChange={handleChange}></input>
<button value="Submit">Submit</button>
</form>
<section>
<NasaImages nasaData={nasaData} loading={loading}/>
</section>
</div>
)
}
export default NasaAPI
Here's where the issue is, in the child component:
import React from 'react'
const NasaImages = ({ nasaData }) => {
console.log(nasaData)
return (
<div>
<h2>This is a where the data go. 👇</h2>
{
nasaData && nasaData.map((data, idx) => {
return (
<div key={idx}>
<p>{data.href}</p>
<div>
{/* {data.links.map((data) => {
return <p>{data.href}</p>
})} */}
{data.data.map((data) => {
return <p>{data.description}</p>
})}
</div>
</div>
)
})
}
</div>
)
}
export default NasaImages
The current configuration works, and will display a data.description (data.data.map) mapping property. However, I want the commented code immediately above it to work which displays a data.href (data.links.map) property.
The JSON looks as follows:
So, the issue is that I can map one set of properties, data.data.map, but cannot access the other in the same object, data.links.map, without getting the error "TypeError: Cannot read property 'map' of undefined". Thank you in advance!
There exists a data element sans a links property, in other words there is some undefined data.links property and you can't map that. Use Optional Chaining operator on data.links when mapping, i.e. data.links?.map. Use this on any potentially undefined nested properties.
const NasaImages = ({ nasaData = [] }) => {
return (
<div>
<h2>This is a where the data go. 👇</h2>
{nasaData.map((data, idx) => (
<div key={idx}>
<p>{data.href}</p>
<div>
{data.links?.map((data, i) => <p key={i}>{data.href}</p>)}
{data.data?.map((data, i) => <p key={i}>{data.description}</p>)}
</div>
</div>
))}
</div>
)
}
I am getting an object from the API ('https://api.covid19api.com/summary'). This object has a
key
Countries with an array of objects and this array of objects I need to filter.
const filteredData = data.Countries.filter(dat => {
return dat.Country.toLowerCase().includes(searchfield.toLowerCase());
})
TypeError: Cannot read property 'Countries' of undefined.
Why an array of objects is not recognized and is not filtered?
In another file, the map method iterates over the same writing data.Countries without error.
const Home = () => {
const [data, setData] = useState();
const [searchfield, setSearchfield] = useState('')
useEffect(() => {
const fetch = async () => {
try{
const res = await axios.get('https://api.covid19api.com/summary');
setData(res.data);
}catch(error){
console.log(error);
}
};
fetch();
}, []);
const onSearchChange = (event) => {
setSearchfield(event.target.value)
}
const filteredData = data.Countries.filter(dat => {
return dat.Country.toLowerCase().includes(searchfield.toLowerCase());
})
return (
<div className="main-container">
<Header searchChange={onSearchChange}/>
<div className="wrapper">
<Card data={data}/>
{/*<div className="graph">
<h1>Global Pandemic Crisis Graph</h1>
<img src={COVID.image} alt=""/>
</div>*/}
<div className="countries">
<Countries data={filteredData}/>
</div>
</div>
{/*<Footer />*/}
</div>
)
}
When you are fetching the data from an api, you need to use optional chaining ? when applying any higher order function to an array just incase the data haven't been loaded. for example
const filteredData = data?.Countries.filter(dat => {
return dat.Country.toLowerCase().includes(searchfield.toLowerCase());
})
Issue
The initial data state is undefined, so data.Countries is undefined on the initial render.
const [data, setData] = useState();
Solution
Provide valid initial state and guard against later bad updates (if they happen).
const [data, setData] = useState({ Countries: [] });
...
const filteredData = data?.Countries?.filter(dat => {
return dat.Country.toLowerCase().includes(searchfield.toLowerCase());
})
You need to filter your data in a callback of the axios response, or it will be "undefined" because it hasn't finished fetching it.
let filteredData = useRef(null);
useEffect(() => {
if (typeof data !== "undefined")
filteredData.current = data.Countries.filter((dat) => {
return dat.Country.toLowerCase().includes(searchfield.toLowerCase());
});
}, [data]);
const fetch = async () => {
const res = await axios
.get("https://api.covid19api.com/summary")
.then((response) => {
// response.data should not be "undefined" here.
setData(response.data);
})
.catch((error) => {
// Error fallback stuff
console.error(error);
});
};
if (!filteredData.current) fetch();
Later in your code you can check whether or not it has been defined,
return (
<div className="main-container">
<div className="wrapper">
{filteredData.current !== null &&
filteredData.current.map((CountryMap, i) =>
<div key={i}>{CountryMap.Country}</div>
)
}
</div>
</div>
);
I am trying to display the name of each of the teams using the following API:https://www.balldontlie.io/api/v1/teams. I have the following code in my main app.js file:
const result = await Axios('https://www.balldontlie.io/api/v1/teams')
console.log(result.data)
console.log(result.data.data[0])
This is successfully able to fetch the data and the first console line is able to display all data in the console while the second line displays all the information about the first team in the data. In each of the teams data information, they have one attribute that is called 'name' and says the team name. I was wondering on how I will be able to call this. When I have the following code in another file it doesn't display anything on the screen:
{Object.keys(items).map(item => (
<h1 key={item}>{item.data}</h1>
))}
What should I change item.data to be able to properly display the names of all the teams? I could provide more code if needed, but I thought this code would probably do.
Don't use axios, now JS has a better alternative called fetch. wrap call on a async function. finally destructor the data object. and void installing more things on your node_modules.
What's here ?
Request endpoint using callApi function
Collect only data from all json scope when finish promise.
Loop over each name
const myComponent = () => {
const names = [];
const callApi = async () => {
await fetch('https://www.balldontlie.io/api/v1/teams')
.then(r => r.json())
.then(resp => { const { data } = resp; return data; })
.catch(e => { console.log(e.message); });
};
callApi();
return <>
{names && names.length > 0
? <span>{names.map(({ id, name }) => <h1 key={id}>{name}</h1>)}</span>
: <span>No rows </span>}
</>;
According to your second block of code, you are trying to access a "data" property of a string, since you are mapping an array of keys of the items.
Effectively:
const item_keys = Object.keys(items);
//if the items is an array, item_keys = ["0","1",...]
//if items is an object, item_keys = ["id","name",...]
const result = item_keys.map(item => (
<h1 key={item}>{item.data}</h1>
));
//either way, result is an array of <h1>undefined</h1>
//because item is a string
Assuming you defined items as const items = result.data.data (edited), you should be able to display names like this:
{items.map((item, index) => (
<h1 key={index}>{item.name}</h1>
))}
const result = await Axios('https://www.balldontlie.io/api/v1/teams')
const data = result.data;
//if you want to display all names
{
data.map(team => (
<div>
<h1 key={`${team.id}`}>{team.name}</h1>
</div>
)
}
//if you want to display all fields in each team
{
data.map(team => (
<div key={`${team.id}`}>
{
Object.keys(team).map((key, keyIndex) => (
<h1 key={`k-${team.id}-${keyIndex}`}>{team[`${key}`]}</h1>
))
}
</div>
)
}
Your response is an array
// you need to change this
Object.keys(items).map(item => (
<h1 key={item}>{item.data}</h1>
))}
// to this where items = result.data.data
items.map(item => <h1 key={item.id}>{item.city} </h1>
// to note here item.city need to be a string or number and not an objet
//if your data does not have an Id
items.map((item, index) => <h1 key={index}>{item.city} </h1>
and in your code this will become somthing like this
return (
<div>
{items.map((item, index) => <h1 key={index}>{item.city} </h1>}
</div>
)
Here an example how the complete code should looks, since you are seeing the response and data in console but not in the UI, it could be a state problem.
const App = () => {
const [items, setItems] = useState([]);
useEffect(()=>{
fetch('https://www.balldontlie.io/api/v1/teams')
.then(res => res.json())
.then(res => {
setItems(res.data);
})
.catch(error => {
//handle error here
});
},[])
return (
<>
{items.map(el => <h1 key={el.id}> el.city </h1>}
</>
)
}
As your data response is an array of objects and your requirement is to display attributes of that data, you can just run a simple loop on data and render items. Below "data" is the response from API
const data = [{"id":"1",name:"Team1"}, {"id":"2",name:"Team2"}]
data.map(item =>
<div>
<h1 key={id}>{item.name}</h1>
</div>
);