Unique "key" prop not working for my array mapping - REACT - reactjs

My unique key is not working for my array mapping.
The Link component is from react-scroll. I've therefore wrapped it into a tag, and setting the key here (thinking this could be where the issue is from)
I understand using index for setting a unique key is bad practice. But I am really just trying to remove the warning message a this point!
Any help would be very much appreciated.
My code:
import styles from '../styles/NavImage.module.css'
import { Link } from 'react-scroll'
const NAVLINKSHOVER = ['About', 'Club', 'Team', 'Timing', 'FAQ']
const NavImage = ({toggleFaq, toggleState, hoveredIndex, setHoveredIndex}) => {
const handleClick = () => {
toggleFaq();
};
return (
<nav className={styles.navbar}>
<div className={styles.linkContainer}>
{
NAVLINKSHOVER.map ((navLink, index) => {
if (navLink === 'FAQ') {
return (
toggleState?(
<div key={index}>
<Link
to="main"
smooth={true}
offset={0}
duration={0}
onClick={handleClick}
className={`${styles.navLinks} ${styles.linkNotActive}`}
>FAQ
</Link>
</div>
):(
<div key={index.toString()}>
<Link
to="faq"
smooth={true}
offset={500}
duration={200}
onClick={handleClick}
className={`${styles.navLinks} ${styles.linkNotActive}`}
>FAQ
</Link>
</div>
)
)
}
return (
<>
<a
className={`${styles.navLinks} ${hoveredIndex === index? styles.linkActive : styles.linkNotActive}`}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(-1)}
key={index.toString()}
>
{navLink}
</a>
{
index!==NAVLINKSHOVER.length-1? <p className={styles.divider}> | </p> : null
}
</>
)
})
}
</div>
</nav>
);
}
export default NavImage;

You're returning a key in the case of FAQ, but not in any other cases; see here?
return (
<> <---- no key
<a
The top component returned needs a key. The string being iterated over would serve nicely without using the index.
{
NAVLINKSHOVER.map ((navLink, index) => {
if (navLink === 'FAQ') {
return (
toggleState?(
<div key="faq-main">
<Link
to="main"
smooth={true}
offset={0}
duration={0}
onClick={handleClick}
className={`${styles.navLinks} ${styles.linkNotActive}`}
>FAQ
</Link>
</div>
):(
<div key="faq">
<Link
to="faq"
smooth={true}
offset={500}
duration={200}
onClick={handleClick}
className={`${styles.navLinks} ${styles.linkNotActive}`}
>FAQ
</Link>
</div>
)
)
}
return (
<React.Fragment key={navLink}>
<a
className={`${styles.navLinks} ${hoveredIndex === index? styles.linkActive : styles.linkNotActive}`}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(-1)}
key={index.toString()}
>
{navLink}
</a>
{
index!==NAVLINKSHOVER.length-1? <p className={styles.divider}> | </p> : null
}
</React.Fragment>
)
})
}

You should wrap the bottom return statement in a div and then give that div a key, if you want to get rid of the warning.
<div key={index.toString()}>
<a
className={`${styles.navLinks} ${
hoveredIndex === index ? styles.linkActive : styles.linkNotActive
}`}
onMouseEnter={() => setHoveredIndex(index)}
onMouseLeave={() => setHoveredIndex(-1)}
>
{navLink}
</a>
{index !== NAVLINKSHOVER.length - 1 ? (
<p className={styles.divider}> | </p>
) : null}
</div>
Pls note it is still a bad practice to use index as a key.

Related

conditional statements in a .map function returns nothing in React

I'm trying to do conditional rendering on react and although the condition it's either true or false, the elements are not being rendered.
What could be the problem? Thanks
<section>
<li className="top-nav__item top-nav__item--active">
{state.profileData[0].likes.map((like) => {
console.log(like) // outputs the user id and appState.user._id
like.user === appState.user._id ? (
<Link
onClick={(e) => startLikingTweet(e, tweetPost)}
to="/"
className="top-nav__link"
>
<IcomoonReact
className="top-nav__icon"
iconSet={iconSet}
color="red"
size={20}
icon="heart"
/>
<span>{tweetPost.likes.length}</span>
</Link>
) : (
<a>hey</a>
);
})}
</li>
</section>
You're missing a return statement inside of your .map, try adding a return before like.user === appState.user._id like the following:
I'm trying to do conditional rendering on react and although the condition it's either true or false, the elements are not being rendered. What could be the problem? Thanks
<section>
<li className="top-nav__item top-nav__item--active">
{state.profileData[0].likes.map((like) => {
console.log(like) // outputs the user id and appState.user._id
return like.user === appState.user._id ? (
<Link
onClick={(e) => startLikingTweet(e, tweetPost)}
to="/"
className="top-nav__link"
>
<IcomoonReact
className="top-nav__icon"
iconSet={iconSet}
color="red"
size={20}
icon="heart"
/>
<span>{tweetPost.likes.length}</span>
</Link>
) : (
<a>hey</a>
);
})}
</li>
</section>

to unique useState variable for repeated item in ReactJS

I am newbie in reactjs. How can set a variable for every single iterating items?
I have some list and when click trash icon i want to show loading icon just for clicked item
const [deleteLoading, setDeleteLoading] = useState(false);
<ul className="mt-2">
{projects && projects.map((project, index) => (
<li key={index}>
<NavLink to={`someurl`}>{project.project_name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span onClick={() => deleteProject(project._id)} className={`${deleteLoading ? "hidden" : ""}`}>
<svg> ..trash icon.. </svg>
</span>
</li>)
)}
</ul>
when i clicked trash button it seems like
const deleteProject = async (id) => {
setDeleteLoading(true)
// some deleting code..
}
The behaviour you have is normal here because you use the same logic for every elements in your map function : ${deleteLoading ? "" : "hidden"}
If you want to have the delete icon only in the good project you should instead define a state var like deletingProject which contain the ID or the name of the deleting project.
Then you just have to modify the loading icon span like this :
<span className={`${project.project_id === deletingProject ? "" : "hidden"}`}>
I would suggest creating a component to hold each project, and add the delete functionality to the individual component. For example:
Project.js
export default function Project({ project, index }) {
const [deleteLoading, setDeleteLoading] = useState(false);
return (
<li key={index}>
<NavLink to={`someurl`}>{project.project_name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span onClick={() => deleteProject(project._id)} className={`${deleteLoading ? "hidden" : ""}`}>
<svg> ..trash icon.. </svg>
</span>
</li>
}
}
And then import into the Parent component
import Project from './Project.js'
export default function Parent(props) {
return (
<ul className="mt-2">
{projects && projects.map((project, index) => (
<Project project={project} index={index}/>
)}
</ul>
)
)}
I hope that helps
If you want to keep each project logic and state separate I'd suggest to create another component and host any project related logic there.
An example
function ProjectItem({ name }) {
const [deleteLoading, setDeleteLoading] = useState(false)
function handleDelete() {
// Some logic here
setDeleteLoading(true)
}
return (
<li>
<NavLink to={`someurl`}>{name}</NavLink>
<span className={`${deleteLoading ? "" : "hidden"}`}>
<svg> ..loading icon.. </svg>
</span>
<span
onClick={handleDelete}
className={`${deleteLoading ? "hidden" : ""}`}
>
<svg> ..trash icon.. </svg>
</span>
</li>
)
}
function MainComponent() {
return (
<ul className="mt-2">
{projects &&
projects.map((project) => (
<ProjectItem key={project._id} name={project.project_name} />
))}
</ul>
)
}
NOTES: Never use index as key prop in React Components. If you have an id already then use that as it's key value. Key prop is intended to be unique among all your rendered components
If you have items that can have it's own state move them to a new component. Hope you end up liking React and learning a lot my man ;3

How can i make a counter for each component in functional component?

Halo everybody, i want to ask for my problem right now..
i want to make a counter in each component list.. i already have a function for handle the counter, but the counter doesnt increment in spesific id. that function increment the counter value for all component.. please help me
there is my code
MenuPage.js
import React, { useEffect, useState } from "react";
import "../../assets/index.css";
import ProductList from "../../components/ProductList";
import { productList } from "../../components/constant/productList";
import NavigationBottom from "../../components/NavigationBottom";
function MenuPage() {
const [showDisplay, setShowDisplay] = useState("grid");
const [quantityProduct, setQuantityProduct] = useState(0);
const handleShowDisplay = (e) => {
setShowDisplay(e);
};
const handleAdd = (value, idx) => {
if (value.id === idx + 1) {
setQuantityProduct(quantityProduct + 1);
} else {
setQuantityProduct(0);
}
};
const handleMinus = () => {
if (quantityProduct === 0) {
return 0;
}
setQuantityProduct(quantityProduct - 1);
};
return (
<div className="main_container">
<div className="container_menu__style">
<div
onClick={() => handleShowDisplay("grid")}
className="button_grid__view"
>
<i class="bi bi-grid"></i>
</div>
<div
onClick={() => handleShowDisplay("list")}
className="button_list__view"
>
<i class="bi bi-list"></i>
</div>
</div>
<div
className={` ${
showDisplay === "grid"
? "container_menu__page"
: "container_menu__page_list"
}`}
>
{productList.map((item, idx) => {
return (
<ProductList
key={idx}
id={idx}
item={item}
quantityProduct={quantityProduct}
showDisplay={showDisplay}
handleAdd={handleAdd}
handleMinus={handleMinus}
/>
);
})}
<NavigationBottom />
</div>
</div>
);
}
export default MenuPage;
ProductList.js
import React from "react";
function ProductList({
id,
item,
quantityProduct,
showDisplay,
handleAdd,
handleMinus,
}) {
return (
<div
className={` ${
showDisplay === "grid"
? "wrapper_menu__product"
: "wrapper_menu__product_list"
}`}
>
<div
className={` ${
showDisplay === "grid"
? "content_menu__product"
: "content_menu__product_list"
}`}
>
<div
className={`${
showDisplay === "grid"
? "image_menu__product"
: "image_menu__product_list"
}`}
>
<img
alt="product-list"
className={` ${showDisplay === "grid" ? "" : "img_list"}`}
src="https://i.pinimg.com/originals/23/91/52/2391523603cbd5153d7eb4e37eb3c882.png"
/>
</div>
<div className="wrapper_menu__detail">
<div className="container_menu">
<div className="menu_title__product">{item.product_name}</div>
<div className="menu_price__product">Rp. {item.product_price}</div>
</div>
<div className="container_quantity">
{/* <div className="quantity_text">Jumlah pesanan</div> */}
<button
className="button_minus__product"
onClick={() => handleMinus(item)}
>
<i class="bi bi-dash"></i>
</button>
<div className="quantity_product">{quantityProduct}</div>
<button
className="button_add__product"
onClick={() => handleAdd(item, id)}
>
<i class="bi bi-plus"></i>
</button>
</div>
{/* <div className="wrapper_quantity__product">
<div className="quantity_product__text">Jumlah Pesanan</div>
<div className="quantity_product__number"></div>
</div> */}
</div>
</div>
</div>
);
}
export default ProductList;
Your MenuPage only has a single quantityProduct (and setQuantityProduct) variable. All items are using and setting this same variable.

WP API in React Js (Gutenberg Editor) : Cannot read property 'wp:featuredmedia' of undefined even with condition

I've built a dynamic block in Gutenberg editor for latest custom posts.
And fetched featuredmedia for them through _embed.
So it's working if all my posts have featured image, but if not I have an error in console:
TypeError: Cannot read property 'wp:featuredmedia' of undefined
So I made condition, but having the same error.
What am I doing wrong? Here is my code:
import { Component } from '#wordpress/element';
import { __ } from '#wordpress/i18n';
import { withSelect } from '#wordpress/data';
class PortfolioPostsEdit extends Component {
render() {
const { posts, className } = this.props;
return (
<>
{(posts && posts.length > 0) ?
<div className={className}>
{posts.map(post => (
<article key={post.id}>
<a href={post.link}>
{post.title.rendered}
</a>
{post._embedded['wp:featuredmedia'] &&
<div className="image">
<img src={post._embedded['wp:featuredmedia'[0].source_url} />
</div>
}
</article>
))}
</div>
: <div>{__("No Posts Found", "my-blocks")}</div>
}
</>
)
}
}
export default withSelect(
(select, props) => {
return {
posts: select('core').getEntityRecords('postType', 'my_portfolio', 'per_page: 20', '_embed: true')
}
}
)(PortfolioPostsEdit);
I can't understand why my condition doesn't work:
{post._embedded['wp:featuredmedia'] &&
<div className="image">
<img src={post._embedded['wp:featuredmedia'[0].source_url} />
</div>
}
Can someone help me, please? Thanks.
This mean post._embedded is undefined. Try this:
post && post._embedded && post._embedded['wp:featuredmedia'] // make sure post._embedded is defined
Thank you!!!! worked great for me, I was puzzled with this for several days....
this is my working code for validating post contain feature image so I wont get any error in case we try to load a post that don't have a feature image set.
post && post._embedded && post._embedded['wp:featuredmedia']&&
{posts && posts.length > 0 ? (
<ul className={className}>
{posts.map(post => (
<li key={post.id}>
<a
target="_blank"
rel="noopener noreferrer"
href={post.link}
>
{decodeEntities(post.title.rendered)}
</a>
{
post && post._embedded && post._embedded['wp:featuredmedia']&&
<>
<img src={post._embedded['wp:featuredmedia'][0].source_url}/>
<span>{post._embedded['wp:featuredmedia'][0].title.rendered}</span>
</>
}
</li>
))}
</ul>
) : (
<div>
{" "}
{posts
? __("No Posts Found", "mytheme-blocks")
: __("Loading...", "mytheme-blocks")}{" "}
</div>
)}

How to do statement in a statement in react?

I need to do a statement and print if the array is not empty, and if it's empty print an error message.
{isEmpty(this.props.items) ? errorMessage : working}
The simple idea is to replace working to:
{this.props.items && this.props.items.map((movie) => (
<Link to={`/movie/${movie.id}`} key={movie.id}>
<img className="Image-movie"
src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={`${movie.title}`}/>
</Link>
))}
I try it to insert it but tells something about code syntax, any idea?
One of many solutions is to asign the second statement to a const
const items = this.props.items ? this.props.items.map((movie) => (
<Link to={`/movie/${movie.id}`} key={movie.id}>
<img className="Image-movie"
src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={`${movie.title}`}/>
</Link>
)) : null
return {isEmpty(this.props.items) ? errorMessage : items}
Since the first check is not necessary you could just do
return !this.props.items.length ? error : this.props.items.map(/*...*/)
I think you can do that,
const {items} = this.props;
{items.length > 0 ? items.map((movie) => (
<Link to={`/movie/${movie.id}`} key={movie.id}>
<img className="Image-movie"
src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={`${movie.title}`}/>
</Link>
)): (<Spinner /> || null) }
why you are checking again for props.items:
const renderItems = this.props.items.map((movie) => (
<Link to={`/movie/${movie.id}`} key={movie.id}>
<img className="Image-movie"
src={`https://image.tmdb.org/t/p/w500/${movie.poster_path}`}
alt={`${movie.title}`}/>
</Link>
))
{isEmpty(this.props.items) ? errorMessage : renderItems}
{this.props.items.length>0? this.props.items.map((movie) => (
<Link to={`/movie/${movie.id}`} key={movie.id}>
<img className="Image-movie"
src={sourcePath}
alt={movie.title}/>
</Link>)): errorMessage}

Resources