I included an Icon while mapping an array and I need to change the icon on that specific iteration onclick, I also want to render the test results when the icon is clicked.
I'm not sure how to get a unique value that is specific to this iteration that will handle this change or is my structure all wrong?
** went to sleep found solution = encapsulation **
map the component so the boolean variable is local scoped to that iteration.
shared solution below
const Students = ({students}) => {
const [showTests, setShowTests] = useState(false);
return (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{showTests && <TestResults results={student.tests} />}
</div>
{showTests ? (
<FontAwesomeIcon
onClick={() => setShowTests(!showTests)}
className="faIcon"
icon={faMinus}
/>
) : (
<FontAwesomeIcon
onClick={() => setShowTests(!showTests)}
className="faIcon"
icon={faPlus}
/>
)}
</div>
);
}
const Main = () => {
return (
<div className="main" id="main">
{filteredStudents.map(student => (
//each iteration is a unique component
<Students student={student} />
))}
</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>
Thanks ahead of time for any suggestions
The return must always have a top-level tag.
const Students = ({ students }) => {
return (
<>
{students.map((student, i) => (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()}{" "}
{student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{faMinus && <TestResults results={student.tests} />}
</div>
<FontAwesomeIcon
id={i}
onClick={(e) => handleTests(e.target.id)}
className="faIcon"
icon={current ? faPlus : faMinus}
/>
</div>
))}
</>
);
};
Let me know if this is what you're thinking. Just store the index in a state variable and compare against it. When they click change the index to the one they clicked on.
const Students = ({students}) => {
const [selectedIndex, setSelectedIndex] = useState(0)
function handleTests(id, index) { //NEW
//Your current code
setSelectedIndex(index) //NEW
}
return (
{students.map((student, i) => (
<div className="wrapper" key={student.id}>
<div className="img-container">
<img src={student.pic} alt="student" />
</div>
<div className="content">
<h1>
{student.firstName.toUpperCase()} {student.lastName.toUpperCase()}
</h1>
<p>Email: {student.email}</p>
<p>Company: {student.company}</p>
<p>Skill: {student.skill}</p>
<p>Average: {averageGrade(student.grades)}%</p>
{selectedIndex == i && <TestResults results={student.tests} />} //NEW
</div>
<FontAwesomeIcon
id={i}
onClick={e => handleTests(e.target.id, i)} //NEW
className="faIcon"
icon={selectedIndex == i ? faPlus : faMinus}
/>
</div>
))}
)
}
Related
I have the following code and I want to apply a conditional rendering, because the index 0 does not exist. I don't want to render the information for this particular index. How can I do it?
return (
<section>
{pokemonCards.map((pokemon, index) =>
<div key={index}>
<img
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${index}.png`}
alt={pokemon.name}/>
<p>{pokemon.name}</p>
</div>
)}
<button
className="get-more-button"
onClick={getMorePokemons}>Get more pokemons
</button>
</section>
)
This should work:
return (
<section>
{pokemonCards.map((pokemon, index) =>
{index === 0 ? null : <div key={index}>
<img
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${index}.png`}
alt={pokemon.name}/>
<p>{pokemon.name}</p>
</div>
)}
<button
className="get-more-button"
onClick={getMorePokemons}>Get more pokemons
</button>}
</section>
)
You could do as below if you do not want to render an empty div for the nonexisting pokemon:
return (
<section>
{pokemonCards.map(
(pokemon, index) =>
index !== 0 && (
<div key={index}>
<img
src={`https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${index}.png`}
alt={pokemon.name}
/>
<p>{pokemon.name}</p>
</div>
)
)}
<button className="get-more-button" onClick={getMorePokemons}>
Get more pokemons
</button>
</section>
);
This piece of code giving me type error: "TypeError: Cannot read property 'map' of undefined"
I don't understand what is the problem here.
const ShowMainData = ({ name, rating, summary, tags, image }) => {
return (
<div>
<img src={image ? image.original : IMG_PLACEHOLDER} alt="show-cover" />
<div>
<div>
<h1>{name}</h1>
<div>
<Star />
<span>{rating.average || 'N/A'}</span>
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: summary }} />
<div>
Tags:{' '}
<div>
{tags.map((tag, i) => (
<span key={i}>{tag}</span>
))}
</div>
</div>
</div>
</div>
);
}
I added?. before map:
const ShowMainData = ({ name, rating, summary, tags, image }) => {
return (
<div>
<img src={image ? image.original : IMG_PLACEHOLDER} alt="show-cover" />
<div>
<div>
<h1>{name}</h1>
<div>
<Star />
<span>{rating.average || 'N/A'}</span>
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: summary }} />
<div>
Tags:{' '}
<div>
{tags?.map((tag, i) => (
<span key={i}>{tag}</span>
))}
</div>
</div>
</div>
</div>
);
}
or you can check if tags is not undifined by doing this:
const ShowMainData = ({ name, rating, summary, tags, image }) => {
return (
<div>
<img src={image ? image.original : IMG_PLACEHOLDER} alt="show-cover" />
<div>
<div>
<h1>{name}</h1>
<div>
<Star />
<span>{rating.average || 'N/A'}</span>
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: summary }} />
<div>
Tags:{' '}
<div>
{tags && tags.map((tag, i) => (
<span key={i}>{tag}</span>
))}
</div>
</div>
</div>
</div>
);
}
Issue happens because your tags variable is undefined, you may not be passing it as a prop. Safer alternative is:
const ShowMainData = ({ name, rating, summary, tags, image }) => {
return (
<div>
<img src={image ? image.original : IMG_PLACEHOLDER} alt="show-cover" />
<div>
<div>
<h1>{name}</h1>
<div>
<Star />
<span>{rating.average || 'N/A'}</span>
</div>
</div>
<div dangerouslySetInnerHTML={{ __html: summary }} />
<div>
Tags:{' '}
<div>
{tags&& tags.length> 0 ? tags.map((tag, i) => (
<span key={i}>{tag}</span>
)):null}
</div>
</div>
</div>
</div>
);
}
can you help me how can I get genre.id when user clicks on Button onClick function ?
Thank you
...
return (
<div className="App">
<Header />
<div className="App-wrapp">
<div className="filter">
{osa &&
osa.map((genre) => (
<Button key={genre.id} {...genre} onClick={thisFunction} />
))}
</div>
<div className="movie-list">
{movies.length > 0 &&
movies.map((movie) => <Movie key={movie.id} {...movie} />)}
<button className="btn" onClick={showMore}>
Load More
</button>
</div>
</div>
</div>
);
}
export default App;
<Button key={genre.id} {...genre} onClick={() => thisFunction(genre.id)} />
I need to target all the components inside map function, but I am only getting the last component inside it.
products?.map((product, i) => (
<div
key={product.id}
className="product__card"
onMouseEnter={() => sliderRef.current.slickNext()}
onMouseLeave={() => sliderRef.current.slickPause()}
>
<StyledSlider {...settings} ref={sliderRef}>
{product.assets.map(({ url, id, filename }) => (
<div className="product__image__container" key={id}>
<img src={url} alt={filename} />
</div>
))}
</StyledSlider>
<div className="product__content__container">
<h3 className="slim__heading">{valueChopper(product.name, 23)}</h3>
<p contentEditable="true" dangerouslySetInnerHTML={{ __html: valueChopper(product.description, 30) }} />
<h6 className="slim__heading">Rs. {product.price.formatted}</h6>
</div>
</div>
)
The simplest way is to use one ref to target multiple elements. See the example below. I've modified your code to make it work.
const sliderRef = useRef([]);
products?.map((product, i) => (
<div
key={product.id}
className="product__card"
onMouseEnter={() => sliderRef.current[i].slickNext()}
onMouseLeave={() => sliderRef.current[i].slickPause()}
>
<StyledSlider {...settings} ref={el => sliderRef.current[i] = el}>
{product.assets.map(({ url, id, filename }) => (
<div className="product__image__container" key={id}>
<img src={url} alt={filename} />
</div>
))}
</StyledSlider>
<div className="product__content__container">
<h3 className="slim__heading">{valueChopper(product.name, 23)}</h3>
<p contentEditable="true" dangerouslySetInnerHTML={{ __html:valueChopper(product.description, 30) }} />
<h6 className="slim__heading">Rs. {product.price.formatted}</h6>
</div>
</div>
)
I'm using map in react to show a feed of facebook posts, but i don't want the post to render if the message field is missing from the json data.
If I do this I get an error ('if' is an unexpected token) and my project won't build
return (
<Slider
{...Settings}>
{postsAvailable && posts.map((post, index) => (
if (!post.message) return null
return (<div key={post.id}>
{ index === 0 && <Item /> }
{ index > 0 && <div className='item'>
<img data-original={post.full_picture} className='img-responsive' />
<div className={`facebook-content slide-${post.id}`}>
<p className='text'>{post.message}</p>
</div>
</div> }
</div>)
))}
</Slider>
)
You can use filter first:
posts.filter((post) => post.message).map((post, index) => (...
Although, if possible you should filter when you get the posts, and not in the render function. Do as little as possible in the render function - for a good overview, and for performance.
You are not giving your function given to map a body. Change () to {} and it will work as expected.
return (
<Slider {...Settings}>
{postsAvailable &&
posts.map((post, index) => {
if (!post.message) return null;
return (
<div key={post.id}>
{index === 0 && <Item />}
{index > 0 && (
<div className="item">
<img
data-original={post.full_picture}
className="img-responsive"
/>
<div className={`facebook-content slide-${post.id}`}>
<p className="text">{post.message}</p>
</div>
</div>
)}
</div>
);
})}
</Slider>
);
Alternatively you could filter out all the posts that don't have a message first and then map those that does.
return (
<Slider {...Settings}>
{postsAvailable &&
posts
.filter(post => post.message)
.map((post, index) => (
<div key={post.id}>
{index === 0 && <Item />}
{index > 0 && (
<div className="item">
<img
data-original={post.full_picture}
className="img-responsive"
/>
<div className={`facebook-content slide-${post.id}`}>
<p className="text">{post.message}</p>
</div>
</div>
)}
</div>
))}
</Slider>
);