React - Only show item if variable is true - reactjs

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.

Related

Trying to show element based on user selection

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

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.

Remove previous content when new one is rendered through a condition (React)

I have a navbar that uses eventKeys to switch between the buttons
const CustomNav = ({ active, onSelect, ...props }) => {
return (
<Nav
{...props}
activeKey={active}
onSelect={onSelect}
style={{ marginBottom: "15px" }}>
<Nav.Item eventKey='all'>All</Nav.Item>
<Nav.Item eventKey='movies'>Movies</Nav.Item>
<Nav.Item eventKey='shows'>Shows</Nav.Item>
<Nav.Item eventKey='people'>People</Nav.Item>
</Nav>
);
};
I did this:
const Content = () => {
if (this.state.active === "all") {
return (
<div>
{trending.results &&
trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (this.state.active === "movies") {
return (
<div>
{trendingMovies.results &&
trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
Called it here:
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}
To display the correct data for each tab, but when I'm on the movies tab it shows all the data from the first "all" tab + data on the "movies" tab. I wanna show each data individually corresponding to the correct tab which is controlled by "this.state.active". Tried a switch statement too and that did not work
you are using the arrow syntax
const Content = () => { ... }
and also using this.state variable in your code !!!
if you want to use this.state, then you want to use the class syntax, like
class Content extends React.Component { ... }
but don't mix the two styles.
what you are probably wanting to do is to send the active variable as a prop
try:
const Content = ({active}) => {
if (active === 'all') {
return (...)
} else if (active === 'movies') {
return (...)
}
return null
}
and where you are calling the component you send the active value in as a prop
<Content active={active} />
Note also that you are using the variables trending and trendingMovies and it is unclear where those come from, you may need to send those via props also.
Now you can also leave the if..else logic outside of your Content component like so
const Content = ({myTrending}) => {
return (
<div>
{myTrending.results &&
myTrending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
and then where you call that component you have
<Content
myTrending={active === 'all' ? trending : trendingMovies}
/>
You need to pass active and other variables as props to the Content component, since it doesn't access them otherwise:
const Content = ({active, trending=[], trendingMovies=[]}) => {
if (active === "all") {
return (
<div>
{trending.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
} else if (active === "movies") {
return (
<div>
{trendingMovies.results.map((i) => (
<React.Fragment key={i.id}>
<p>{i.title}</p>
</React.Fragment>
))}
</div>
);
}
};
return (
<div className='Home'>
<FlexboxGrid justify='center'>
<Panel bordered header='Trending today!'>
<CustomNav
className='customNav'
appearance='subtle'
active={active}
onSelect={this.handleType}
/>
<Content active={this.state.active} trending={this.state.trending} trendingMovies={this.state.trendingMovies} />
<Pagination
{...this.state}
style={{ marginTop: "15px" }}
maxButtons={5}
size='sm'
pages={totalPages}
activePage={this.state.activePage}
onSelect={this.handlePage}
/>
</Panel>
</FlexboxGrid>
</div>
);
}
}

Cart value resets on browser refresh

In ECommerce React project, I've created cart when clicked, changes to 'In Cart' and is then disabled which shows the product is in cart and can't be clicked back, but, when browser is refreshed Cart value resets back.
Following is the code reference
Product.js
export default class Product extends Component {
render() {
const {id, title, img, price, inCart} = this.props.product;
const dataValue = JSON.parse(localStorage.getItem('myCart'))
return (
<ProductWrapper className="col-9 mx-auto col-sm-6 col-lg-3 my-3 " >
<div className="card" >
<ProductConsumer>
{(value) => (
<div className="img-container p-3" >
<img style={imageSize} src={img} alt="product"
className="card-img-top center img-fluid img-responsive"/>
<button className="cart-btn" disabled={inCart?true:false}
onClick={() => {value.addToCart(id)}}>
{console.log('DATA VALUE', dataValue)}
{ inCart ? (
<p className="text-capitalize mb-0" disabled>
{" "}
In Cart</p>
) : (
<i className="fas fa-shopping-cart"/>
)}
</button>
</div>)}
</ProductConsumer>
</div>
</ProductWrapper>
);
}
}
ProductList.js (Mapping list of products)
export default class ProductList extends Component {
render() {
return (
<React.Fragment>
<ProductConsumer>
{value => {
return value.products.map((product, key) => {
return <Product key={product.id} product={product} />;
});
}}
</ProductConsumer>
</React.Fragment>
);
}
}
App.js
class App extends Component {
render() {
return (
<React.Fragment>
<Route render={({location}) => (
<Switch location={location}>
<Route exact path="/" component={ProductList}/>
</Switch>
)} />
</React.Fragment>
);
}
}
export default App;
I've tried with localStorage but no effect. What can be done to make the cart value store in localStorage, so that when refreshed 'In Cart' remains. Any appropriate solution?
Following is the codesandbox link: https://codesandbox.io/s/tdgwm
<button
className="cart-btn"
disabled={inCart ? true : false}
onClick={() => {
value.addToCart(id);
localStorage.setItem("added", "In Cart");
console.log(localStorage.getItem("added"));
}}
>
{localStorage === null ? (
<i className="fas fa-shopping-cart" />
) : (
<p className="text-capitalize mb-0" disabled>
{" "}
{localStorage.getItem("added")}
</p>
)}
</button>
I took a quick look at this. The above code will get the localStorage item set and render the in cart value. However, I will leave it to you to apply it to the individual product and update the conditionality for the icon/in cart piece. I'll be on for a little bit longer if you have any questions. But basically you need to see if localstorage is set prior to rendering.
It is because you don't check the local storage inCart value in the Product.js
I have added another condition like below and check it within inCart in the conditions
...
...
const localInCart = dataValue
&& dataValue.find(i => i.id === id)
&& dataValue.find(i => i.id === id).inCart;
...
...
...
(inCart || localInCart) ? ...)
you can see the result on codesandbox; https://codesandbox.io/s/mobile-store-ufxgp?fontsize=14&hidenavigation=1&theme=dark

Conditionally add reference inside loop

I am conditionally assigning difference references (ref=) to elements inside a loop depending on index. I know the below is wrong (although works), how should I refactor?
const SortableItem = SortableElement(({ value, index }) => (
<div className="grid-item">
{index == 0 && (
<img className="image-item" src={value.imgUrl} ref={this.bigImage} />
)}
{index == 1 && (
<img className="image-item" src={value.imgUrl} ref={this.secondImage} />
)}
{index > 1 && <img className="image-item" src={value.imgUrl} />}
</div>
));
You can use ternary operator to assign a value based on index. Also make sure to use === when comparing values:
const SortableItem = SortableElement(({ value, index }) => {
const ref = index === 0 ? this.bigImage : index === 1 ? this.secondImage : null;
return (
<div className="grid-item">
<img className="image-item" src={value.imgUrl} ref={ref} />
</div>
);
});

Resources