Why infinite-scroll in react don't see data from function map()? - javascript

I have a problem with this code:
<InfiniteScroll
dataLength={beers.length}
next={fetchMoreBeers}
hasMore={true}
loader={<p>Loading ...</p>}
endMessage={<p id="beers-end">No more beers :(</p>}
>
{beers.map((beer, index) => (
<div key={index}>
<Link
to={{
pathname: `/beer/${beer.id}`,
state: { beer: beer.id },
}}
>
<div className="beer-wrapper">
<div className="beer">
<img
className="beer-img"
src={beer.image_url}
alt={beer.name}
/>
<p className="beer-name">
{beer.name.length < 15
? `${beer.name}`
: `${beer.name.substring(0, 20)}...`}
</p>
<p className="beer-tagline">
{beer.tagline.length < 20
? `${beer.tagline}`
: `${beer.tagline.substring(0, 25)}...`}
</p>
</div>
</div>
</Link>
<Route path="/beer/:id" component={Beer} />
</div>
))}
</InfiniteScroll>;
And when the page is scrolled the error occurs:
"TypeError: Cannot read property 'id' of undefined"
for this line:
pathname: `/beer/${beer.id}`,
This seems like InfiniteScroll doesn't see the data from map() function ....
Maybe some of you know how to fix this problem?
Thanks for any tips!

Perhaps the issue is with template literals. When we add template literals directly it throws such issues. I experienced the same in my projects.
May be you can try in below way
render(){
const { beers } = this.props;
const beerItems = beers.map((beer, index) => {
let pathName = `/beer/${beer.id}`;
return (
<div key={index}>
<Link to={{
pathname: {pathName},
state: {"beer": beer.id}
}} >
<div className="beer-wrapper">
<div className="beer">
<img className="beer-img" src={beer.image_url} alt={beer.name} />
<p className="beer-name">
{beer.name.length < 15 ? beer.name : beer.name.substring(0, 20) }
</p>
<p className="beer-tagline">
{beer.tagline.length < 20 ? beer.tagline : beer.tagline.substring(0, 25) }
</p>
</div>
</div>
</Link>
<Route path="/beer/:id" component={Beer} />
</div>
)
}
return(
<InfiniteScroll
dataLength={beers.length}
next={fetchMoreBeers}
hasMore={true}
loader={<p>Loading ...</p>}
endMessage={<p id="beers-end">No more beers :(</p>}
>
{beerItems}
</InfiniteScroll>
)
}

Maybe there is an undefined value stored in beers array. Try to filter them out, like so:
beers.filter(Boolean).map((beer, index) => (
// ...
)
Also, I've noticed that hasMore prop is always set to true. To see if it helps, try something like:
<InfiniteScroll
dataLength={beers.length}
next={fetchMoreBeers}
hasMore={beers.length < 25} // or hasMore={this.state.hasMore}
loader={<p>Loading ...</p>}
endMessage={<p id="beers-end">No more beers :(</p>}
> ...
EDIT:
You are using Array.from({ length: 20 })). The output array will contain 20 undefined values. That is why you should consider beers.filter(Boolean).
I suppose this is what you really meant:
this.setState({
items: this.state.beers.concat(Array.from(this.state.beers))
})
or:
this.setState({
items: [...this.state.beers, ...this.state.beers]
})

You should check for the status code when fetching the beers. fetch will still let you do your thing even if the request failed. If you exceed your endpoint data limit, you'll get this data added to your beer array:
{
"statusCode": 400,
"error": "Bad Request",
"message": "Invalid query params",
"data": [{
"param": "per_page",
"msg": "Must be a number greater than 0 and less than 80",
"value": "100"
}]
}
As you can see there's no id param there which makes this strange error to show up.
Including a json schema validator (like https://ajv.js.org/) to make sure the data is in expected format would be a perfect choice.

I think that this problem is because of data records limitations in this Api .

Related

My react app is crashing even after error handling. Why is this happening?

I have created a react component that will display movie details after getting details from the TMDb API. The app working perfectly but there is one condition I am trying to handle i.e. when the movie is not found. In that, I case I want my background to be white instead of the movie poster. I want the same thing for title, overview, rating etc. I have used ternary operator for this.
However, my app still crashes this: -
Uncaught TypeError: Cannot read properties of undefined (reading 'backdrop_path')
at HeroArea
Here is my code: -
import React, { useState } from 'react'
import MovieDetailModal from '../MovieDetailsModal/MovieDetailModal';
import './HeroArea.css';
function HeroArea({ movie }) {
const [displayModal, setDisplayModal] = useState(false);
const displayMovieModal = () => setDisplayModal(true);
//Default background if movie.backdrop isn't found
const backdropImage = movie.backdrop_path !== null ?
{ backgroundImage: `url(https://image.tmdb.org/t/p/original/${movie.backdrop_path})` }
: {backgroundColor : "white"};
return (
<>
<MovieDetailModal status={displayModal} movie={movie} setStatus={setDisplayModal} />
<div className="hero-container" style={backdropImage} >
<div className="content-width info-container">
<div className="inner-container">
<h1>{movie.title ? movie.title : "No results found!"}</h1>
<p>{movie.overview ? movie.overview.substring(0, 250) : ""}...</p>
<button
onClick={displayMovieModal}
className="common-button view-more-button-hero">Display more</button>
</div>
</div>
</div>
</>
)
}
export default HeroArea
The short answer is, because movie is undefined, you can't access its properties.
You would need to make sure that movie is set before anything else for example:
const backdropImage = movie.backdrop_path !== null
? { backgroundImage:`url(https://image.tmdb.org/t/p/original/${movie.backdrop_path})` }
: {backgroundColor : "white"};
BECOMES
const backdropImage = movie && movie.backdrop_path !== null
? { backgroundImage:`url(https://image.tmdb.org/t/p/original/${movie.backdrop_path})` }
: {backgroundColor : "white"};
This also means you will need to make sure you only render this component if movie is set. for example:
return (
<>
<MovieDetailModal status={displayModal} movie={movie} setStatus={setDisplayModal} />
<div className="hero-container" style={backdropImage} >
<div className="content-width info-container">
<div className="inner-container">
<h1>{movie.title ? movie.title : "No results found!"}</h1>
<p>{movie.overview ? movie.overview.substring(0, 250) : ""}...</p>
<button
onClick={displayMovieModal}
className="common-button view-more-button-hero">Display more</button>
</div>
</div>
</div>
</>
)
BECOMES
if(movie){
return (
<>
<MovieDetailModal status={displayModal} movie={movie} setStatus={setDisplayModal} />
<div className="hero-container" style={backdropImage} >
<div className="content-width info-container">
<div className="inner-container">
<h1>{movie.title ? movie.title : "No results found!"}</h1>
<p>{movie.overview ? movie.overview.substring(0, 250) : ""}...</p>
<button
onClick={displayMovieModal}
className="common-button view-more-button-hero">Display more</button>
</div>
</div>
</div>
</>
)
}
return null;

How to display data fetched from Wordpress RestAPI in html format using React?

Using React, I am trying to get page data from a WordPress API.
pic of my current output
As you can see, the date and title data are displayed normally but the excerpt is not rendered properly. I am not sure how I can fix that.
Below is the code I use to fetch and display the data:
To fetch the data:
//fetching the pages
useEffect( ()=>{
Axios.get("https://www.eswaran.no/wp-json/wp/v2/pages").
then(response => {
setPosts(response.data);
}, [posts, setPosts]);
})
To display the data:
<div className="page-list">
{posts && posts.length && posts.map((post, index) => {
return (
<div key={post.id} className="page">
<h2>{post.title.rendered}</h2>
<h4>{moment(post.date).format('Do MMMM YYYY')}</h4>
<div dangerouslySetInnerHTML={{ __html: post.excerpt.rendered}} />
Go to page
</div>
);
})}
</div>
Ok your Api is well returning the data.
So in order to just remove the brackets, you can use regex :
{posts && posts.length && posts.map((post, index) => {
const cleanExcerpt = post.excerpt.rendered.replace(/\[([^\[])*(\])/g, '');
return (
<div key={post.id} className="page">
<h2>{post.title.rendered}</h2>
<h4>{moment(post.date).format('Do MMMM YYYY')}</h4>
<div dangerouslySetInnerHTML={{ __html: cleanExcerpt }} />
Go to page
</div>
);
})}

JSX this.state.STATE.map is not a function

Issue: this.state.previousWords.map() is nor recognized as a function
I have a hangman app that is trying to render previously seen words onto the page in their own line. The issue happens when a new word is added to the array in the state previousWords: []. When the page tries to re-render this it errors stating "TypeError: this.state.previousWords.map is not a function." Below is the code snippet where the error is happening.
Based off a prior project this is correct syntax (code has been included). I need help understanding why this is not recognized and how to fix it.
<div className="col-3">
<h3>Previous words:</h3>
<br></br>
{this.state.previousWords.length ? (
<div>
{this.state.previousWords.map( (value, index) => (
<div
key={index}
className="row">
{value}
</div>
))}
</div>
):(<></>)}
</div>
Prior app with functioning .map statement:
{this.state.events.length ? (
<Dropdown.Menu>
{this.state.events.map(events => (
<Dropdown.Item
key={events.event_id}
/* TODO: onClick() */
>
{events.title}
</Dropdown.Item>
))}
</Dropdown.Menu>
) : (
<Dropdown.Menu>
<Dropdown.Item>No Events Available</Dropdown.Item>
</Dropdown.Menu>
)}
exact error
325 |
326 | <div className="col-3">
327 | <h3>Previous words:</h3>
> 328 | <br></br>
| ^ 329 | <>{console.log(this.state.previousWords)}</>
330 |
331 | { this.state.previousWords.length ? (
Setting the state for previousWord:
if( this.loss() ) {
let previousWords = [...this.state.previousWords];
previousWords.push(this.state.apiWord);
console.log("Loss: before state updated: this.state.previousWords: ", this.state.previousWords);
console.log("Loss: before state updated: let previousWords: ", previousWords);
this.setState({
previousWords: this.state.apiWord,
pageLock: true,
losses: this.state.losses + 1,
}, () => {
console.log("Loss: after setState: this.state.previousWords: ", this.state.previousWords);
this.resetGame();
setTimeout(() => {
// this.setWord();
}, 5000);
});
Console log where the state is being set. "test" was added to the initial state for testing purposes.
after API call: this.state.previousWords: ["test"]
App.js:209 Loss: before state updated: this.state.previousWords: ["test"]
App.js:210 Loss: before state updated: let previousWords: (2) ["test", "unintermitting"]
It looks like you correctly build out the new array, but then do not use it to update state:
let previousWords = [...this.state.previousWords];
previousWords.push(this.state.apiWord);
this.setState({
previousWords: this.state.apiWord, // Should be previousWords: previousWords
pageLock: true,
losses: this.state.losses + 1,
},
this.state.previousWords is not an Array that's why you're getting the error.
Easiest way to prevent it - check if this.state.previousWords is an Array Array.isArray(this.state.previousWords) and only then use map.
Array.isArray(this.state.previousWords) && this.state.previousWords.length

Flattening nested dictionary React? - Cannot read property 'standard' of undefined

I am working on a personal WebApp project in React. I am new to this technology but very eager to learn it. I came across a problem. I am using axios to fetch data from Google Youtube API response and it works but I am unable to parse obtained data that is nested. What i mean by that:
Inside Items there are multiple snippets
{
"items": [
{
"snippet": {
"title": "Dolby Atmos - usłysz więcej!",
"description": "W dzisiejszym odcinku opowiem wam o tym czym jest nagłośnienie i system dolby atmos. System i nagłośnienie Dolby atmos znajdziemy obecnie w najlepszych kinach. System wspierają takie filmy jak \"Zjawa\" czy \"Kapitan Ameryka wojna bohaterów\". Jakość dźwięk docenią kinomani i prawdziwi audiofile. Istnieje również stworzenia systemu składającego się z głośników dolby atmos kina domowego, ale jest poważna inwestycja.\nJeżeli jesteś z Łodzi i chcesz poczuć Dolby Atmos na własnej skórze kliknij tutaj:\nhttp://www.helios.pl/47,Lodz/StronaGlowna/\n\nJeżeli dzisiejszym odcinek Ci się spodobał zostaw like'a albo subskrybcję :D\nFanPage:\nhttp://facebook.com/RuchOpornikow\nGoogle+:\nhttps://plus.google.com/u/0/+RuchOpor...\nTwitter:\nhttps://twitter.com/RuchOpornikow",
"thumbnails": {
"standard": {
"url": "https://i.ytimg.com/vi/QWTk3vnztRw/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://i.ytimg.com/vi/QWTk3vnztRw/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"resourceId": {
"videoId": "QWTk3vnztRw"
}
}
},
I want to get a random snippet from items and use it's title attribute, description and thumbnails.
At this point I can access description and title but accessing movie.description.thumbnails.standard.url or movie.resourceId.videoId gives me an error.
TypeError: Cannot read property 'standard' of undefined
31 | backgroundPosition: "center center",
32 | }}
33 | >
> 34 | <img src ={`${movie.thumbnails.standard.url}`}/>
35 | <div className="banner_contents">
36 | {/* edge cases */}
37 | <h1 className="banner_title">
Here is my full code :
function Banner() {
const [movie, setMovie] = useState([]);
useEffect(() => {
async function fetchData() {
const request = await axios.get("./data.json");
setMovie(
request.data.items[
Math.floor(Math.random() * (request.data.items.length-1))
].snippet
);
return request;
}
fetchData();
}, []);
return (
<header
className="banner"
style={{
backgroundSize: "cover",
// backgroundImage: `url("${movie.snippet.thumbnails.maxres.url}")`,
backgroundPosition: "center center",
}}
>
<img src ={`${movie.thumbnails.standard.url}`}/>
<div className="banner_contents">
{/* edge cases */}
<h1 className="banner_title">
{movie.title}
</h1>
<div className="banner_buttons">
<button className="banner_button">Play</button>
<button className="banner_button">Original</button>
</div>
<h1 className="banner_description">{movie.description}</h1>
</div>
<div className="banner--fadeBottom" />
</header>
);
}
export default Banner;
Do you know what could be an error and how to fix it? Console.log and JSON.stringify show that those attributes are there.
Issue(s)
Your initial movie state is an array, but you reference into it like it is an object in your render logic. movie.snippet and movie.thumbnails will obviously be undefined.
const [movie, setMovie] = useState([]);
and
movie.thumbnails.standard.url
Solution
Make the initial state type match what it is updated to from the fetch request and how it's accessed in the render logic, i.e. an object
const [movie, setMovie] = useState({});
Use proper null checks and provide fallback values.
Null Checks
movie.thumbnails && movie.thumbnails.standard && movie.thumbnails.standard.url || ""
or using Optional Chaining and Null Coalescing
movie?.thumbnails?.standard?.url ?? ""
you are getting the error because before getting any data your movie object is empty . you should check before accessing nested properties . like this
<img src ={movie.thumbnails ? `${movie.thumbnails.standard.url}` : ""}/>
movie?.thumbnails?.standard?.url should do it. Looks like movie.thumbnails is undefined
assuming movie contains the correct data, the syntax should work. Here's a test: https://jsfiddle.net/yf470pma/1/
I would add a breakpoint and check what is really there.
initial movie is an empty array and it will take some time to fetch from API. refactor to the below
const [movie, setMovie] = useState();
and check movie if it's not null before rendering
return movie?(
<header
className="banner"
style={{
backgroundSize: "cover",
// backgroundImage: `url("${movie.snippet.thumbnails.maxres.url}")`,
backgroundPosition: "center center",
}}
>
<img src ={`${movie.thumbnails.standard.url}`}/>
<div className="banner_contents">
{/* edge cases */}
<h1 className="banner_title">
{movie.title}
</h1>
<div className="banner_buttons">
<button className="banner_button">Play</button>
<button className="banner_button">Original</button>
</div>
<h1 className="banner_description">{movie.description}</h1>
</div>
<div className="banner--fadeBottom" />
</header>
):null;

Use props from component in another file

As you might understand, I'm pretty new to React. I tried to search StackOverflow and google this, but I cannot find anything that answers this question.
I'm creating this travel review page. I save a form to a Firebase database and I display it using a component. However, I want to display, say, only the name of a country, but I cannot manage to do that. I'm pretty sure that I must pass it as a prop but I cannot figure out how to do that, exactly.
What I want is to, use this component and decide in the component what to view.
If I'd use this component in some other .js-file, I'd like to do something like this:
and display only the data that lies withing the field countryName.I'm going to use the country names as a list, that's why I want to single out just the names.
I've played around using props but I cannot get my head around it. Perhaps someone could help out?
Thanks.
This is the function which I use as a component:
const CountryList = () => {
const countries = useCountries()
return (
<div className="countries">
{countries.map((country) =>
<div key={country.id}>
<div className="time-entry">
Name of review: {country.revName} <br/>
Name of Country: {country.countryName}<br/>
Destination 1: {country.dest1}<br/>
Destination 2: {country.dest2}<br/>
Destination 3: {country.dest3}<br/>
Beer price: {country.beerPrice}<br/>
Food price: {country.foodPrice}<br/>
Hostel price: {country.hostelPrice}<br/>
Review: {country.review}<br />
<img src={country.url} alt="no-img" />
</div>
</div>
)}
</div>
)
}
Super simple way could be to pass an array of "fields" you want to display as a prop, and conditionally render UI. If the displayFields array includes the name of the country property then render that "field".
Example:
const CountryList = ({ displayFields = [] }) => {
const countries = useCountries();
return (
<div className="countries">
{countries.map(country => (
<div key={country.id}>
<div className="time-entry">
{displayFields.includes("revName") && (
<div>Name of review: {country.revName}</div>
)}
{displayFields.includes("countryName") && (
<div>Name of Country: {country.countryName}</div>
)}
{displayFields.includes("dest1") && (
<div>Destination 1: {country.dest1}</div>
)}
{displayFields.includes("dest2") && (
<div>Destination 1: {country.dest2}</div>
)}
{displayFields.includes("dest3") && (
<div>Destination 1: {country.dest3}</div>
)}
{displayFields.includes("beerPrice") && (
<div>Beer price: {country.beerPrice}</div>
)}
{displayFields.includes("foodPrice") && (
<div>Food price: {country.foodPrice}</div>
)}
{displayFields.includes("hostelPrice") && (
<div>Hostel price: {country.hostelPrice}</div>
)}
{displayFields.includes("review") && <div>Review: {country.review}</div>}
{displayFields.includes("imgUrl") && <img src={country.url} alt="no-img" />}
</div>
</div>
))}
</div>
);
};
Usage:
<CountryList displayFields={["countryName", "beerPrice"]} />

Categories

Resources