ReactJS problem when calling function inside .map at jsx block code - javascript

I am trying to call to an arrow function inside jsx statement while using .map() js function:
<ul className="list-group col-10 ms-5">
{datoPaises.map((item) => (
<li className="list-group-item" key={item.id}>
{this.parseRegions(item)}
<span> Muertes nuevas: {item.today_new_deaths} </span>
<span>Muertes totales: {item.today_deaths}</span>
</li>
))}
</ul>
this.parseRegions(item) is returning TypeError: Cannot read property 'parseRegions' of undefined
which is not happening when just writing the item inside the jsx block
<ul className="list-group col-10 ms-5">
{datoPaises.map((item) => (
<li className="list-group-item" key={item.id}>
{item.id}
<span> Muertes nuevas: {item.today_new_deaths} </span>
<span>Muertes totales: {item.today_deaths}</span>
</li>
))}
</ul>
this one display all data correctly, the problem appears when trying to use the function.

Related

How to change .jsx component style from inside the .js file? React

function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false)
if(shownavcontents){
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
}else{
document.getElementsByClassName("navbardivofmobiledevice").style.display = "block";
}
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
As you see I am trying to make responsive navbar, in this case, for mobile devices. I've faced one problem. I've made button on top of navbar and some navbar contents which I want to display only whenever user will click this button and vice versa. So I tried using hooks to check if the user clicked the button which works perfectly, only thing that doesn't works is this if else statements it seems like document.getElementsByClassName("navbardivofmobiledevice").style.display = "none"; doesn't have an effect here. So my question is what is the alternative of this? What can I do here?
This is imperative code:
document.getElementsByClassName("navbardivofmobiledevice").style.display = "none";
With React, you rarely get references to DOM elements and update them manually, and in any case, you do it using Refs, not with the getElement... or querySelector... methods). Instead, you write declarative code and let React take care of the DOM updates for you.
In this case, simply add or remove a hidden attribute or CSS class that has display: none from your JSX:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
<div className="navbardivofmobiledevice" hidden={ !shownavcontents }>
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
</>
);
}
If you prefer to use a class, assuming you have defined a CSS class .isHidden { display: none; } you would use this line instead:
<div className={ `navbardivofmobiledevice${ shownavcontents ? '' : ' isHidden' }` }>
Regarding what some comments are mentioning about rendering that conditionally like so:
function Navbar() {
const [shownavcontents, setShownavcontents] = useState(false);
return (
<>
<div className="top">
<Searchbar />
<AiOutlineMenu size={20} className="outlinemenu" onClick={() => {setShownavcontents(true)}} />
</div>
{ shownavcontents && (
<div className="navbardivofmobiledevice">
<ul>
<li>
Home
</li>
<li>
Members
</li>
<li>
All Posts
</li>
<li>
My Posts
</li>
</ul>
</div>
) }
</>
);
}
I would avoid that, as hiding your main navigation from Google and other search engines will harm your SEO. You need to hide it visually but still have it in the DOM.
If you want to do better than that, add all the appropriate ARIA attributes and logic for a navigation menu with nested submenus, as explained here:
https://www.w3.org/WAI/ARIA/apg/example-index/menubar/menubar-navigation

Reactjs: Cannot read URL property of null, fetching from an API

I have products in json format that are fetched and shown in the frontend. In my products.json there is an image url for each products, but only some have image urls in them, others are empty. When I am looping the data in react I always get error in my react app saying Cannot read property of null in the tag, how do I write a logic that only renders the image when there is an image source else just return an empty div?
<ul>
{this.state.items.map((items, index) => {
return (
<li className="ProductList-product" key={items.id}>
<h3>{items.title}</h3>
<p>{items.description}</p>
<div className="price-box">
<p>from: {items.price} $</p>
</div>
<div>
{<img src={items.photo} alt=""/>}
{/* {console.log(items.photo.id)} */}
</div>
</li>
);
})}
</ul>
replace your
{<img src={items.photo} alt=""/>}
with
{items.photo && <img src={items.photo} alt=""/>}
it will only render img element when item.photo is not null.
You can set a condition. For example:
<ul>
{this.state.items.map((items, index) => {
return (
<li className="ProductList-product" key={items.id}>
<h3>{items.title}</h3>
<p>{items.description}</p>
<div className="price-box">
<p>from: {items.price} $</p>
</div>
{items.photo
? <div>
{<img src={items.photo} alt=""/>}
{/* {console.log(items.photo.id)} */}
</div>
: <div></div>
</li>
);
})}
</ul>

Mapping array inside array with React

I have a database in MongoDB and one of the props in the document contains an array. I'm trying to map the collection in the client-side using React using this code:
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{recipe.ingredients}<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
ingredients prop is an array and I want to map it too. How can I do it?
Any help would be great, thanks!
Just map it the same way you do with recipes
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>
{recipe.category}<br/>
{recipe.ingredients.map(ingredient => (
<span>{ingredient}</span>
))}
<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
just map it again
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>
{recipe.category}<br/>
{recipe.ingredients.map(k =>(
<span key={k._id}>{k}<span>
)<br/>
{recipe.preparation}
</li>
)}
</ul>
</div>
<div>
<h2>Recipes</h2>
<ul>
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{ recipe.ingredients.map(ingredient =>
<span>{ingredient}</span>
)}
<br/>{recipe.preparation}
</li>
)}
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
You can also use .map() there by calling recipe.ingredients.map() as the following:
{this.state.recipes.map(recipe =>
<li key={recipe._id}>
{recipe.title}<br/>{recipe.category}<br/>
{
recipe.ingredients.map((elem, index) =>
<div key={index}>
{elem /* or maybe elem['your-property-name'] if it's not a string */}
</div>
)
}<br/>{recipe.preparation}
</li>
)}
Also don't forget to add key={index} there to avoid the warning based on Lists and Keys.

React js - pass value to child to parent to another child

I am trying to pass the data from Child > parent > child
Child
{this.state.data.map((item, index) => (
<li className='card' key={index}>
<span>{item.continent} </span>
<ul className="accordion-body">
{item.regions.map((c, i) => (
<li key={i} onClick={this.props.toggleContent}>
<img src={c.flag}/> {c.country}
</li>
))}
</ul>
</li>
))}
Basically I need to get selected country and some other values from the child and pass to parent
and pass those values to another child.
My Parent
<div className="modal-header">
<h2>Choose your {title}</h2>
<a href="#" className="model-close" data-dismiss="modal" aria-label="Close"><i
className="fa fa-times-circle"></i></a>
</div>
<div className="modal-body">
{showCountry && <CountryList toggleContent={this.toggleContent}/>}
{showLanguages && <RegionList country={country} flag={flag} languages={languages}
toggleContent={this.toggleContentRegion.bind(this)}/>}
</div>
and
toggleContent = () => {
this.setState({
...this.state,
showCountry: !this.state.showCountry,
showLanguages: !this.state.showLanguages,
title: 'language',
country: 'country',
languages: [],
flag: 'flag'
});
}
I tried to use below
<li key={i} onClick={this.props.toggleContent(c.country)}>
<img src={c.flag}/> {c.country}
</li>
and access it from parent
toggleContent = (country) => {
this.setState({
...this.state,
showCountry: !this.state.showCountry,
showLanguages: !this.state.showLanguages,
title: 'language',
country: country,
languages: [],
flag: 'flag'
});
}
But, my components not working correctly When do that and always shows the 2 child component.
Are there any proper way to pass the data to parent from a json array?
So the best way I would handle this would be to make the import your parent class components into the child , place it at the very top of the child JSX but hide it by default. The modal would be fixed, background covering the full page and at a z-index higher than the rest of the child components, so that way only the modal contents are the only accessible things . You would have a state that "toggles on" the modal for each click of the item list and a close button that toggles it off. You would update the modal content and toggle it on for every click
In terms of the second child, you can just show it on the same modal
Found a way to do this :)
render() {
var toggleContent = this.props.toggleContent;
return (
<div className="modal-wrapper">
<ul className="country-list">
{this.state.data.map((item, index) => (
<li className='card' key={index}>
<span>{item.continent} </span>
<ul className="accordion-body">
{item.regions.map((c, i) => (
**<li key={i} onClick={() => toggleContent(c.country,c.flag, c.languages, c.region)} >**
<img src={c.flag}/> {c.country}
</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
Changed below line
onClick={() => toggleContent(c.country,c.flag, c.languages, c.region)

React variable evaluation when 'push' JSX elements in an array

I'm pushing JSX elements in an array
for (category of this.state.categories) {
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category+'Connectors'}
>
</ul>
</li>
);
}
Problem is that category variable in this.showHideConnectorNames(category) method call evaluates to last category in this.state.categories array (so 'cat2' for all elements). It is evaluated correctly at all the other places, such as inside the <label>.
So I have to do this:
for (category of this.state.categories) {
if (category === 'cat1')
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames('cat1')}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
else if (category === 'cat2')
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames('cat2')}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
else
categories.push(
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
>
</ul>
</li>
);
}
Is this a React issue or am I doing something wrong?
Not sure what you want to do with the array after it's built, but I think using a map call would be quite a bit more efficient. Just place this inside your return statement wherever you want to render your list items:
<ul>
{this.state.categories.map(cat => (
// your li has a missing key property
<li>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(cat)}
>
{cat}
</label>
<ul
className="tree ul-no-style"
id={cat + 'Connectors'}
></ul>
</li>
))}
</ul>
You are using for of incorrectly to prevent using it incorrectly I like to use map instead:
const categories = this.state.categories.map(category => (
// not sure what to use for key here but it's missing
<li key={category.id}>
<label
className="tree-toggler nav-header list-group-item"
onClick={() => this.showHideConnectorNames(category)}
>
{category}
</label>
<ul
className="tree ul-no-style"
id={category + 'Connectors'}
></ul>
</li>
));
This is because category is set in every loop iteration so after the loop is finished category is the last one. That is why by the time you click on your component the category is always the last one from this.state categories.
Here is a demo showing the bahavior:
let category,categories=[],cats=[1,2,3];
//you re assing category every time
for (category of cats) {
categories.push(
()=>console.log('category is now:',category)
);
}
//now category is the last one
console.log('category after loop:',category);
//now I execute the functions category is the last one
categories.forEach(cat=>cat())
just add let, then category will be local inside for scope
for (let category of this.state.categories)

Categories

Resources