Trying to show element based on user selection - reactjs

Here I have my div that contains Two Options ["Book" , "prices"] .
My goal is when the user select the Book option it would show the element for the book , same thing for the price one too .
here is my code :
const BooksandPrices = props => {
const option =["Book" , "Prices"]
return (
<div>
{option.map((option) => (
<div
className='bp'
onClick={(e) => {
{option == 'Book' && <PriceTable />}
{option == 'Prices' && <BookTable />}
}}
>
{option}
</div>
))}
</div>
)
}
return (
<BooksandPrices />
)

If my presumption is correct that and are the elements for each respective option, then you'll want to move them outside of the option.map and initialize a state variable to control which element is displayed
const BooksandPrices = props => {
const [selectedOption, setSelectedOption] = useState();
const option =["Book" , "Prices"]
return (
<div>
{option.map((option) => (
<div
className='bp'
onClick={() => setSelectedOption(option)}
>
{option}
</div>
))}
{selectedOption == 'Book' && <PriceTable />}
{selectedOption == 'Prices' && <BookTable />}
</div>
)
}
return (
<BooksandPrices />
)

React Hooks could solve this, you just need to add useState hook.
Also keep in mind that you should to add key attribute when you loop through an array.
const BooksandPrices = props => {
const option =["Book" , "Prices"];
const [checkedOption, setCheckedOption] = useState("Book");
return (
<div>
{option.map((option, idx) => (
<div
className='bp'
onClick={()=>setCheckedOption(option)}
key={idx}
>
{option}
</div>
))}
<div>
{checkedOption === 'Book' ? <BookTable />:<PriceTable /> }
</div>
</div>
)
}

Related

React - Only show item if variable is true

I want to only show each instance of the FeedTag if let soldOut is false. How do I add this condition to each instance of the FeedTag? Each product in the productList has a qty and soldout prop and I only want to show a <FeedTag> where qty !== soldout.
<div>
{productList.length > 0 &&
productList.map((product, i) => {
let soldOut = (productList.qty = productList.soldout);
return (
<FeedTag
data={product}
name={product.headline}
onSelect={(e) => onSelectProduct(e)}
checked={seletedProductList.includes(product._id)}
icon={<img src={helper.CampaignProductImagePath + product.image} alt="" />}
/>
);
})}
</div>
Fix your assignment of soldOut (using ===; I also renamed the variable to available) and use conditional rendering:
...
const available = productList.qty !== productList.soldout;
return (
available && <FeedTag ... />
);
);
Filter out the products that are not sold out first, then map:
<div>
{productList
.filter((product) => product.qty !== product.soldout)
.map((product) => (
<FeedTag
key={product._id}
data={product}
name={product.headline}
onSelect={(e) => onSelectProduct(e)}
checked={seletedProductList.includes(product._id)}
icon={
<img src={helper.CampaignProductImagePath + product.image} alt='' />
}
/>
))}
</div>
Also don't forget the key attribute inside the map.

How to useState() when I click on an element that is part of an array of elements, only affecting that element

// Using a boolean data type as state
const [layerOne, setLayerOne] = useState(false)
// Looping three elements and I want to modify only the element that I click
{[1, 2, 3].map((o, i) => (
<div
key={i}
id={`opt-${i}`}
className="option"
onClick={() => setLayerOne(!layerOne)}
>
{layerOne ? <BsEyeSlashFill size={21} /> : <BsEyeFill size={21} />}
<div className="option__tittle">
{layerOne ? 'hide' : 'show'} layer
</div>
</div>
))}
In the code above I am trying to modify only the element where I click. In case I decided to use a specific boolean state for each item in the map().
How can I achieve this solution?
You need to make each child element have it's own state, like this:
const SetLayer = () => {
const [layerOne, setLayerOne] = useState(false);
return (
<div
key={i}
id={`opt-${i}`}
className="option"
onClick={() => setLayerOne(!layerOne)}
>
{layerOne ? <BsEyeSlashFill size={21} /> : <BsEyeFill size={21} />}
<div className="option__tittle">
{layerOne ? 'hide' : 'show'} layer
</div>
</div>
)
}
function App() {
return (
[1, 2, 3].map((o, i) => (
<div
key={i}
id={`opt-${i}`}
>
<SetLayer />
</div>
))
)
}

How to pass the list "key" value as a props in nested component?

(I apologize for the ugly code in advance -- currently refactoring)
I'm making a Table of content where the nested content appear when I click on its parent component.
For my logic, I need to pass the value of the list key to its children but I keep receiving an undefined error or nothing at all. I tried to pass the value like this: key={node2.objectId} and keyId={node2.objectId}
I read the specifications on how to pass the key value as a prop here and here
Yet, nothing works.
Here's my code:
import React from "react";
const TocContent = (props) => {
return (
<div className="">
{props.TOC.map((header) => (
<ul
key={header.objectId}
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) =>
props.handleHeaderClick(
header.level,
header.treepath,
header.containsLaw,
header.sections,
header.secNum,
header.objectId,
header.id,
e.stopPropagation(),
)
}
className="TOC TOCsection"
>
{header._id}
{props.headerIndex === header.objectId
? props.headers2.map((node2) => (
<HeaderList
key={node2.objectId}
header={node2}
props={props}
keyId={node2.objectId}
>
{console.log(props.keyId)}
//--problem is here-- {props.headerIndex2 === props.keyId
? props.headers3.map((node3) => (
<HeaderList
key={node3.objectId}
header={node3}
props={props}
>
{props.headerIndex3 === node3.objectId
? props.headers4.map((node4) => (
<HeaderList
header={node4}
key={node4.objectId}
props={props}
/>
))
: null}
</HeaderList>
))
: null}
</HeaderList>
))
: null}
</ul>
))}
</div>
);
};
const HeaderList = ({ header, props }) => {
return (
<ul
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) =>
props.handleHeaderClick(
header.level,
header.treepath,
header.containsLaw,
header.sections,
header.secNum,
header.objectId,
header.id,
e.stopPropagation(),
)
}
>
{header._id}
</ul>
);
};
export default TocContent;
I finally resorted to change the structure a bit. Instead of the code above, I opted to render the HeaderList component directly in its own component (as a child of itself). This way, I'm able to read header.objectId and make the code shorter.
Here's the new code:
import React from "react";
const TocContent = (props) => {
return (
<div className="">
{props.TOC.map((header) => (
<HeaderList key={header.objectId} header={header} props={props} />
))}
</div>
);
};
const HeaderList = ({ header, props }) => {
return (
<ul
onMouseDown={(e) => e.stopPropagation()}
onClick={(e) =>
props.handleHeaderClick(
header.level,
header.treepath,
header.containsLaw,
header.sections,
header.secNum,
header.objectId,
header.id,
e.stopPropagation(),
)
}
>
{header._id}
{/* // if savedIndex === CurrentParent Index */}
{props.headerIndex === header.objectId &&
props.headers2.map((node2) => (
<HeaderList key={node2.objectId} header={node2} props={props} />
))}
{props.headerIndex2 === header.objectId &&
props.headers3.map((node3) => (
<HeaderList key={node3.objectId} header={node3} props={props} />
))}
{props.headerIndex3 === header.objectId &&
props.headers4.map((node4) => (
<HeaderList header={node4} key={node4.objectId} props={props} />
))}
</ul>
);
};
export default TocContent;
I understand this is maybe not the cleanest code, but an improvement nonetheless. If someone wants to propose something better, it will be much appreciated.

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>
);
}

onMouseEnter active on all mapped elements

I have a simple component in which I map through an array of objects and display a li element for each object:
const [isHover, setHover] = useState(false);
return (
<ResultList>
{ props.movieList.length === 0 ? (<NoResults>No results were found...</NoResults>) : (null)}
{ props.movieList.map(movie => {
return (
<React.Fragment>
<li
key={movie.id}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}>
<p>
<span>{movie.original_title}</span>
</p>
{isHover && <AddMovieToDashboard />}
</li>
)
})}
</ResultList>
)
When I onMouseEnter on a li element I want to display another component AddMovieToDashboard. I've set this up by using the useState hook but as you might expect; this causes each li in the ul to display the AddMovieToDashboard component, and not just the one which is being hovered. I should probably use the unique key value from the li element somewhere but I can't find a good example how to do it.
How about moving the items to a separate component so they can have their own state:
function ResultListItem(props) {
const [isHover, setHover] = useState(false);
return (
<li
key={props.movie.id}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<p>
<span>{props.movie.original_title}</span>
</p>
{isHover && <AddMovieToDashboard />}
</li>
)
}
function Component(props) {
return (
<ResultList>
{ props.movieList.length === 0 ? (<NoResults>No results were found...</NoResults>) : (null) }
{ props.movieList.map(movie => {
return (
<ResultListItem movie={movie} />
)
})}
</ResultList>
)
}

Resources