Where is the missing key for my child comp? - reactjs

I've been getting a "Each child in a list should have a unique key prop" error. I've read up on the documentation and put keys in, but no luck. Not sure what I'm misunderstanding!
blogPosts is imported from a context.
The ids being called as keys are unique (POSTID1, POSTID2, ...etc).
Got rid of some classnames for better readability.
const topicPosts = blogPosts.filter((post)=>post.topic.toLowerCase()===topic);
const handleLoadMore = () => {
const newIndex = articleIndex + 5 > topicPosts.length ? topicPosts.length : articleIndex + 5;
setArticleIndex(newIndex);
}
return (
<section onClick={handleOutsideClick}>
<div>
<section>
<h1>{topic}</h1>
{
topicsData.map((topicData)=> topicData.name.toLowerCase()===topic && <p key={topicData.id}>{topicData.intro}</p>)
}
</section>
<div>
<section>
<div>
{
topicPosts.slice(0,3).map((post)=>{
const { id } = post;
return (
<TopicPagePost key={id} {...post}/>
)
})
}
</div>
<div className={topicPosts.length>3 ? `subpage-articles` : `display-none`}>
{
topicPosts.slice(3,articleIndex).map((post, index)=>{
const { id } = post;
return (
<>
<TopicPagePost key={id} {...post}/>
{
index%4===0 && <div key={index} className="subpage-main-ad">
Ad placement here
</div>
}
</>
)
})
}
</div>
<button type="button" onClick={handleLoadMore}>Load more articles</button>
</section>
<aside>
<SubscribeBlock/>
<div>
Ad placement here
</div>
</aside>
</div>
</div>
</section>
)

That is because you need to add a key to the first element returned from the map, in this case <>.
To do that, you need to replace <> with a <Fragment> instead, so you can add a key directly to it:
import React, { Fragment } from "react"
{topicPosts.slice(3,articleIndex).map((post, index) => {
const { id } = post;
return (
<Fragment key={id}>
<TopicPagePost {...post}/>
{index%4===0 && (
<div className="subpage-main-ad">
Ad placement here
</div>
)}
</Fragment>
)
})}

Related

Why it is showing unreachable code after return?

The statement after return (Where I am sending props to ProList , in the mapping section) is showing unreachable. I am still learning react. Can anyone help me in this regard?
import "./Workcard.css"
import React from 'react'
import ProList from "./ProList"
import projcarddata from "./ProlistData"
const Workcard = () => {
return (
<div className="work-container">
<h1 className="project-heading">Projects</h1>
<div className="project-container">
{
projcarddata.map((val,ind) =>
{
return
(
<ProList key={ind} imgsrc={val.imgsrc} title={val.title} text={val.text} view={val.view} source={val.source}/>
);
}
)
}
</div>
</div>
);
};
export default Workcard
`
This can be fixed in two ways
Way 1: remove the braces after the return
Workcard = () => {
return (
<div className="work-container">
<h1 className="project-heading">Projects</h1>
<div className="project-container">
{projcarddata.map((val, ind) => {
return <ProList key={ind} imgsrc={val.imgsrc} title={val.title} text={val.text} view={val.view} source={val.source} />;
})}
</div>
</div>
);
};
```
Way 2: remove the return statement and directly add the return value after the arrow
```
Workcard = () => {
return (
<div className="work-container">
<h1 className="project-heading">Projects</h1>
<div className="project-container">
{projcarddata.map((val, ind) => (
<ProList key={ind} imgsrc={val.imgsrc} title={val.title} text={val.text} view={val.view} source={val.source} />
))}
</div>
</div>
);
};
```
It's because of Automatic semicolon insertion .....
It would turn as return; ....
so instead turn it as below (see the above link for more info)
return(
....
)

while using map() function props.data is undefined

its shows data without map function in console
but whenever I use map function it shows props.data is undifned and also undifined in console
I have used the same code for another page and that works
const Test_Footer = (props) => {
console.log("ok", props.data)
const newvar =props.data.map((item) => {
return (
<>
<li>{item.data.id}</li>
</>
)
})
// console.log(newvar)
return (
<div>
<div class="main-content">
<footer className="footer">
<div className="review-checkbox"><input type="checkbox" /><label>Review</label></div>
<div className="question-nav">
<ul className="pagination">
{newvar}
</ul>
<button className="minimize-btn ml-10"><img src="images/minimize-btn.png" /></button>
</div>
</footer>
</div>
</div >
)
}
export default Test_Footer
const newvar = props && props.data && props.data.map((item) => {
return (
<>
<li>{item.data.id}</li>
</>
)
})

Force update to make functional component re-render

I'm doing pokedex (pokemon wiki stuff). I want to change my component view, when clicking on pokemon images (description lookalike). When I click on an image - nothing happens (firstly, I want at least pokemon's name to be added to the pokemonDescription array). What am I doing wrong?
let pokemonDescription = [];
const useForceUpdate = () => {
const [value, setValue] = useState(true);
return () => setValue(value => !value);
}
const forceUpdate = useForceUpdate();
const onPokemonClick = (event) => {
console.log(
"wrapper clicked, event.target - ",
event.target.getAttribute('data-name')
);
pokemonDescription = [];
pokemonDescription.push(event.target.getAttribute('data-name'));
console.log("description array -", pokemonDescription);
forceUpdate();
};
useEffect(() => {
document.querySelector(".wrapper").addEventListener("click", onPokemonClick);
...
return () => {
document.querySelector(".wrapper").removeEventListener("click", onPokemonClick);
};
}, []);
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)
You should add pokemonDescription to your component state
const [pokemonDescription, setPokemonDescription] = useState([]);
Remove the forceUpdate function and hook, it is unnecessary.
Attach the click handlers to the elements with the data-name attribute you are trying to handle.
Map the pokemonDescription state array to renderable JSX. I simply used a div, but you should use whatever your UI design requires.
const onPokemonClick = (event) => {
setPokemonDescription(names => [
...names,
event.target.getAttribute('data-name'),
]);
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
data-name={p.name}
onClick={onPokemonClick} // <-- attach click handler to img element
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{pokemonDescription.map(name => (
<div>{name}</div>
))}
</div>
</div>
)
Add pokemonDescription to state instead of some local variable and it will solve your issue.
Try to avoid using forceUpdate, most of the times it means only that you are doing something silly.
I don't what that useForceUpdate does , but here is how would go about adding pokemon names to description array which is a state variable in my answer
const [pokemonDescription , setPokemonDescription ] = useState(null);
const onPokemonClick = (p) => {
const tempPokemonDescription = [...pokemonDescription ];
pokemonDescription.push(p.name);
console.log("description array -", pokemonDescription);
setPokemonDescription(tempPokemonDescription )
};
...
return (
<div className="Pokemons">
<div className="column pokemons-list">
<div className="wrapper">
{
pokemonsData.map((p, id) => (
<div className="box" onClick={e=>onPokemonClick(p)} key={ id }>
<img
src={ p.sprites.front_default }
alt="pokemon-img"
title={ p.name }
className="icon"
/>
{ p.name}
<div className="container">
{ pokemonsTypes[id] }
</div>
</div>
))
}
</div>
...
</div>
<div className="column description">
{ pokemonDescription }
</div>
</div>
)

React child not re-rendered when parents props change

I'm having some issues with child re-rendering, I pass methods to children to see if a button should be displayed or not but when the state of the parent changes, the children are not re-rendered.
I tried with the disabled attribute for the button but didn't work either.
Here's my code (I removed unnecessary part):
function Cards(props) {
const isCardInDeck = (translationKey) => {
return props.deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
props.deck.push(card);
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
var index = props.deck.findIndex(
(c) => c.translationKey === card.translationKey
);
props.deck.splice(index, 1);
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}
function Card(props) {
const toggleShowDescription = () => {
if (!showDescription) {
setShowDescription(!showDescription);
}
};
return (
<div onClick={toggleShowDescription} onBlur={toggleShowDescription} >
<img src={"../images/cards/" + props.card.image} alt={props.card.image + " not found"} />
{showDescription ? (
<div className="customCardDetail">
<div className="cardName"></div>
<div className="cardType">
{props.addToDeckDisabled ? (
<Button onClick={() => { props.removeFromDeckClick(props.card);}} startIcon={<RemoveIcon />}>
Remove from deck
</Button>
) : (
<Button onClick={() => { props.addToDeckClick(props.card); }} startIcon={<AddIcon />}>
Add to deck
</Button>
)}
</div>
<div className="cardDescription">
<span>
<FormattedMessage id={props.card.description} defaultMessage={props.card.description} />
</span>
</div>
</div>
) : (
""
)}
</div>
);
}
You code does not update state. Cards mutates the props that it is receiving.
To use state in a functional component in React you should use the useState hook.
Cards would then look something like this:
function Cards(props) {
const [deck, setDeck] = useState(props.initialDeck)
const isCardInDeck = (translationKey) => {
return deck.some(
(card) => !!card && card.translationKey === translationKey
);
};
const addToDeck = (card) => {
if (!isCardInDeck(card.translationKey) && !!card) {
setDeck([...deck, card])
}
};
const removeFromDeck = (card) => {
if (isCardInDeck(card.translationKey) && !!card) {
setDeck(deck.filter(deckItem => deckItem.translationKey !== card.translationKey))
}
};
return (
<div className="cardsContent">
<div className="cards">
{cardList.length > 0 ? (
cardList.map((item, index) => {
return (
<Card key={index} card={item} addToDeckDisabled={isCardInDeck(item.translationKey)} addToDeckClick={addToDeck} removeFromDeckClick={removeFromDeck} />
);
})
) : (
<span>
<FormattedMessage id="app.cards.label.no.card.found" defaultMessage="No card found with filter."/>
</span>
)}
</div>
</div>
);
}

JSX conditional rendering

I'm making my first React-Redux project.
I'd like to change the structure below simply.
const PresentationalComponent = ({
params,
query
}) => {
if (query.title === undefined) {
return (
<div>
<article>
<h2>{params.title}</h2>
<hr></hr>
<p>{params.content}</p>
</article>
</div>
);
} else {
return (
<div>
<div>
<article>
<h2>{query.title}</h2>
<hr></hr>
<p>{query.content}</p>
</article>
</div>
</div>
);
}
};
export default HomeDetail;
This is what I've tried. But it occurs error.
<article>
<h2>{query.title === undefined ? {item.title} : {query.title}}</h2>
Can we make it simple?
const PresentationalComponent = ({ params, query }) => (
<div>
<article>
<h2>{query.title ? query.title : params.title}</h2>
<hr></hr>
<p>{query.title ? query.content : params.content}</p>
</article>
</div>
);

Resources