React - Prevent child re-rendering in map - javascript

I have a re-render issue with a slider which appears in every element from a map.
const Attractions = () => {
const [slide, setSlide] = useState(0)
const handleSlider = (direction) => {
if(direction === "right"){
setSlide(slide + 1)
}else{
setSlide(slide - 1)
}
}
const translation = slide * - 110
return (
<div className="attractions">
<Navbar />
<Header nav={"attractions"}/>
<div className="attractionsContainer">
<div className="attractionsWrapper">
<div className="left">
</div>
<div className="right">
{attractionList.map(attraction => (
<div className="attractionWrapper" key={attraction.id}>
<div className="top">
<div className="topLeft">
<img src="https://holidaystoswitzerland.com/wp-content/uploads/2019/09/Lauterbrunnen.jpg.webp" alt="" className="avatarImg"/>
</div>
<div className="topRight">
<div className="title">{attraction.name}</div>
<div className="location"><LocationOnOutlinedIcon/>{attraction.location.city}, {attraction.location.country}</div>
</div>
<div className="moreInfo">
<MoreVertOutlinedIcon/>
</div>
</div>
<div className="middle">
<div className="sliderWrapper">
{slide > 0 &&
<div className="left" onClick={()=>handleSlider("left")}>
<ArrowBackIosNewOutlinedIcon style={{fontSize: "30px"}}/>
</div>
}
<div className="slider" style={{transform: `translateX(${translation}%)`}}>
{attraction.img.map(img =>(
<img src={img} alt="" />
))}
</div>
{slide < 2 &&
<div className="right" onClick={()=>handleSlider("right")}>
<ArrowForwardIosOutlinedIcon style={{fontSize: "30px"}}/>
</div>
}
</div>
</div>
<div className="bottom">
<div className="interactions">
<FavoriteBorderOutlinedIcon className="actionBtn"/>
<ChatBubbleOutlineOutlinedIcon className="actionBtn"/>
<CheckCircleOutlineRoundedIcon className="actionBtn"/>
<AddCircleOutlineOutlinedIcon className="actionBtn" />
</div>
<div className="description">
{attraction.description}
</div>
<div className="comments">
{attraction.comments.map(comment =>(
<div className="commentItem">
<h4>{comment.user}</h4>
<span>{comment.comment}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
</div>
</div>
<Footer />
</div>
)
}
When i get more elements from the attractionList array, whenever i click on the left or right arrow to go to the next slide, all of the sliders from the list will do the same. I figured out that i should use something so that the slide is set only based on the item from the map, but honestly i have no idea how.

If you want them to behave differently, then the attraction object should have its own slide variable and setSlide method. That way you can just put:
onclick = {() => attraction.setSlide(attraction.slide + 1)}

Related

How do I use Toggle method in Reactjs ? I have added sandbox link at the bottom

In this code, I want to toggle active class to icons div, I watched some youtube videos and they showed this method which is not what I want, this method does not add activeclass to a particular div it adds active class to every div that has icons class. I want whenever someone click onto the div active class should be added into it and when clicked again that class should get removed.
export default function Site() {
const [notActive, setActive] = useState(false);
const switchActive = () => {
notActive ? setActive(false) : setActive(true);
};
return (
<div id="container">
<nav id="navbar">
<a href="j" className="link">
Buzzify
</a>
<a href="f">
<FaRegUserCircle className="user-icon" />
</a>
</nav>
<div id="playlist-container">
<div id="playlist">
<div
className={notActive ? "icons active" : "icons"}
onClick={switchActive}
>
<BsCloudRain />
<VolSlider />
</div>
<div
className={notActive ? "icons active" : "icons"}
onClick={switchActive}
>
<GiForest />
</div>
<div className="icons">
<MdOutlineWaterDrop />
</div>
<div className="icons">
<BiWind />
</div>
<div className="icons">
<GiCampfire />
</div>
<div className="icons">
<GiThreeLeaves />
</div>
<div className="icons">
<BsMoonStars />
</div>
<div className="icons">
<BiWater />
</div>
<div className="icons">
<BiTrain />
</div>
<div className="icons">
<BiCoffeeTogo />
</div>
<div className="icons">
<FaFan />
</div>
<div className="icons">
<DiDigitalOcean />
</div>
<div className="icons">
<GiWaterfall />
</div>
<div className="icons">
<FaPlane />
</div>
<div className="icons">
<IoIosPlanet />
</div>
<div className="icons">
<GiOctopus />
</div>
</div>
</div>
</div>
);
}
CodeSandbox.io
You have two options in order to solve this:
1- You need a state for each icon that keeps track of if you clicked them or not. This could be done by making a React component that represents an icon and then use it to in your Site component:
export default function MyIcon({icon}) {
const [isActive, setIsActive] = useState(false)
return <div className={isActive ? "icons active" : "icons"}
onClick={() => {setIsActive(!isActive)}}>
{icon}
</div>
}
You can then use this component in Site() like this:
<MyIcon icon={<CertainIcon/>} />
2- You can programmatically toggle the classname "active" using the onClick event without relying on a state like this:
<div className="icons"
onClick={(event) => {event.currentTarget.classList.toggle('active')}}>
<CertainIcon/>
</div>
you should manage state per Icon like
const IconWithState = ({children}) => {
const [isActive, setIsactive] = useState(false)
const toggle = () => setIsactive(curr => !curr)
return (
<div
className={active ? "icons active" : "icons"}
onClick={toggle}
>
{children}
</div>
)
and then use it like
...
<div id="playlist-container">
<div id="playlist">
<IconWithState>
<BsCloudRain />
<VolSlider />
</IconWithState>
<IconWithState>
<GiForest />
</IconWithState>
....

ReactJs force input focus on div button click not working

I'm working on a modal in react where I have to click a button outside an input element and focus the input element and change the value. I set the input element to be disabled on default and for the edit button to remove the disabled property and then focus the element. the element is not getting visible focused neither is the disabled property being removed. It looks like it happens in a split second and then returns back to normal because whenever I check if the element is being focused on my console it displays true but It's not being visible focused.
below is my code
EDIT FUNCTION
const [editState,setEditState] = useState(null)
const editCatName = (e,id)=>{
e.stopPropagation();
setEditState({...editState,id: categories[id].id})
setActiveButton('editcatname');
let row = document.querySelectorAll('.cat-row')[id].querySelector('input');
console.log('row',row)
row.classList.remove('pointer');
row.value='Row value set'
row.hasAttribute('disabled') && row.toggleAttribute('disabled')
row.focus();
console.log(row.value)
}
const [activeButton,setActiveButton] = useState('newproduct');
const ProductCategoryModal = ()=>{
return(
<div
onClick={()=>{setCategoryModal(false)}}
className="_modal fixed " style={{zIndex:'100'}} >
<div
onClick={e=>e.stopPropagation()}
className='wytBg boxRad paddBoth sectPad relative'>
<div>
<div className="flexBtw">
<h3 className="head flexAl">Product Categories</h3>
<div className='head'> <AiOutlinePlus/> </div>
</div>
</div>
<GridStyle className='nestM' gridp='45% 55%' gap='0em'>
<Scrolldiv className='' measure='300px'>
{
categories && categories.length?
categories.map((ctg,key)=>
ctg.name !=='Others' &&
<div key={key}
onClick={(e)=>
fixCategoryDetail(ctg.id,e)
}
className={`cat-row cursor rowws flexBtw ${ key===0 && 'rows-
top'}`}>
<div>
<input
defaultValue={ctg.name}
// disabled
style={{maxWidth:'180px'}}
className={'categoryInput pointer '+ key}
onChange={e=>setEditState({
id:ctg.id,
value:e.target.value
})}
onBlur={e=>{
e.target.value=ctg.name
}}
/>
{/* {ctg.name} */}
</div>
<div className="smallflex hashEdit">
<div>
<BiEditAlt
className='cursor'
FUNCTION TO FOCUS ELEMENT IS HERE
onClick={e=>editCatName(e,key)}
/>
</div>
{
!products.find(prod=>prod.category ===ctg.id) &&
<div className='hash'> <CgClose className='cursor'/> </div>
}
</div>
</div>
):null
}
</Scrolldiv>
<Scrolldiv measure='300px' className='secondRow paddBoth'>
{
categoryDetail.length?
<div className=" search flexC">
<div>
<SearchInputBox
icon={ <CgSearch style={{width:30}} />}
padLeft={'10px'}
className='searchInput smalltxt'
inputPadding='0.5em 0.6em'
// onchange={filterByStarts}
iconPosition
summary='Type or search a product'
options=
{products.filter(prod=>
prod.category===3).map(prod=>prod.name)}
inputClassName={'searchInput smalltxt'}
pad='0'
/>
<div className="nestM flex">
<div measure='150px' >
{
categoryDetail.length?
categoryDetail.map((ctg,key)=>
ctg.category !== 3 &&
<div key={key} className=
{`doubleflex flexBtw smalltxt
itemunits ${key!==0 &&
'smallM'} `}>
<div>{ctg.name}</div>
<div>
<CgClose onClick=
{()=>removeProductFromCategory
(ctg.id,ctg.name)}
className='cursor'/>
</div>
</div>
):null
}
</div>
</div>
</div>
</div>
:null
}
</Scrolldiv>
</GridStyle>
<div className="section flexC">
<button style={{maxWidth:'70%'}}
onClick={()=>
activeButton==='newproduct'?removeProductFromCategory():
activeButton==='addcategory'?createNewCategory():
editCategoryName()}
className="submit-button category-submit">
Update
</button>
</div>
</div>
</div>
)
}

Javascript variables in css are not working

In this code i am passing both imageurl and size which are javascript variables but i am using in css.
css is not considering both. How to resolve this issue ??
const MenuItem = ({title, imageUrl, size}) => {
return(
<div className="${size} menu-item">
<div className="background-image" style={{backgroundImage: "url(${imageUrl})"}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
);
}
This should work:
const MenuItem = ({title, imageUrl, size}) => {
return(
<div className={`${size} menu-item`}>
<div className="background-image" style={{backgroundImage: "url(${imageUrl})"}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
);
}
To use variables inside a string like this, you need to use the following syntax:
className={`${size} menu-item`}
You should wrap them in back-ticks (``):
const MenuItem = ({title, imageUrl, size}) => {
return(
<div className=`${size} menu-item`>
<div className="background-image" style={{backgroundImage: `${url(${imageUrl})}`}} />
<div className="content">
<h1 className="title">{title.toUpperCase()}</h1>
<span className="subtitle">SHOP NOW</span>
</div>
</div>
);
}

next js mapping with-portals

I'm looping through JSON data to create a grid of cards, clicking on a card will open a modal using portals. I used the Zeit example - https://github.com/zeit/next.js/tree/master/examples/with-portals
My map loop works for the cards, they are displaying as expected, but whatever card is clicked on, the data in the modal is always for the last card in the JSON file.
{ activities.map(activity => (
<div key={activity.id}>
<div className="grid-item" onClick={event => setOpen(true)}>
<div className="card">
<div className="card-image" style={{background: 'url(' + activity.image + ')'}}></div>
<div className="card-content">
<h2>{activity.name}</h2>
<div className="card-footer">
<div className="age-range">{activity.ageFrom + ' - ' + activity.ageTo + ' yrs'}</div>
<div className="location">{activity.city}</div>
</div>
</div>
</div>
</div>
{open && (
<ClientOnlyPortal selector="#modal">
<div className="backdrop">
<div className="modal">
<button type="button" onClick={event => setOpen(false)}>Close</button>
<Details
id={activity.id}
name={activity.name}
website={activity.website}
category={activity.category}
image={activity.image}
image={activity.longDesc}
/>
</div>
</div>
</ClientOnlyPortal>
)}
</div>
)
)
}

How to mapStateToProps only after the component receives data from Firebase

In the application I'm trying to render data, based on the route parameters.
When I run the code, I receive an error screen because mapStateToProps hasn't received data from Firestore and the .find() function cannot run on 'undefined'.
const mapStateToProps = (state, ownProps) => {
const id = ownProps.match.params.product_id;
return {
product: state.firestore.ordered.products.find(p => p.id === id)
};
};
I've tried using conditional rendering inside the component itself but the error is inside the mapStateToProps(). The data doesn't even get to the component because the script is stopped at the first attempt of mapping the state to its props.
Here is my component without conditional rendering, for reference.
<div className="product-detail-page top-padding container col-lg-10">
<div className="product-desc">
<div className="product-desc-image col-lg-5">
<img
src={this.props.product.image}
alt={this.props.product.name}
className="col-12"
/>
</div>
<div className="product-desc-right col-lg-7">
<h2 className="product-desc-title">{this.props.product.name}</h2>
<div className="product-desc-bottom-section">
<div className="product-desc-text">
<p>
In stock - Ships from Ireland. Sold and Shipped by{" "}
<span className="comp-logo">
<Link to="/">
comp<span className="red-dot">.</span>com
</Link>
</span>
</p>
<ul>
<li>{this.randomDetails(1)}</li>
<li>{this.randomDetails(2)}</li>
<li>{this.randomDetails(3)}</li>
<li>{this.randomDetails(4)}</li>
<li>{this.randomDetails(5)}</li>
<li>{this.randomDetails(6)}</li>
</ul>
<div className="star-rating">
<img src={this.starRating(1)} alt="*" />
<img src={this.starRating(2)} alt="*" />
<img src={this.starRating(3)} alt="*" />
<img src={this.starRating(4)} alt="*" />
<img src={this.starRating(5)} alt="x" />
</div>
</div>
<div className="product-desc-checkout">
<div className="product-desc-total">
<h4>€ {this.preTaxCalculator().toFixed(2)}</h4>
<p>VAT - € {this.taxCalculator().toFixed(2)}</p>
<p>Shipping: € {this.props.product.shipping}</p>
<h3>{this.props.product.price}</h3>
</div>
<div className="product-desc-action-buttons">ADD TO CART</div>
<div className="product-desc-action-buttons">
ADD TO WISHLIST
</div>
<div className="product-desc-action-buttons">
SAVE FOR LATER
</div>
</div>
</div>
</div>
</div>
<div className="product-details">
<h2>Learn more about the {this.props.product.name}</h2>
<table>
<tbody>
{this.renderKeys().map(key => (
<tr key={key}>
<td>{key}</td>
<td>{this.props.product.details[key]}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
You should use a conditional in your mapStateToProps function. Check whether you have the data or not, and return an empty array, object, or the thing that suits you best, if the data is not available. When the state changes, the function maps again with the real deal.
you could use condition to check for props and render only if available. Note the changes and also make use of timeout to delay the return
const mapStateToProps = (state, ownProps) => {
const id = ownProps.match.params.product_id;
setTimeout(()=>
return {
product: state.firestore.ordered.products.find(p => p.id === id)
}),3000)
};
{this.props.product && <div className="product-detail-page top-padding container col-lg-10">
<div className="product-desc">
<div className="product-desc-image col-lg-5">
<img
src={this.props.product.image}
alt={this.props.product.name}
className="col-12"
/>
</div>
<div className="product-desc-right col-lg-7">
<h2 className="product-desc-title">{this.props.product.name}</h2>
<div className="product-desc-bottom-section">
<div className="product-desc-text">
<p>
In stock - Ships from Ireland. Sold and Shipped by{" "}
<span className="comp-logo">
<Link to="/">
comp<span className="red-dot">.</span>com
</Link>
</span>
</p>
<ul>
<li>{this.randomDetails(1)}</li>
<li>{this.randomDetails(2)}</li>
<li>{this.randomDetails(3)}</li>
<li>{this.randomDetails(4)}</li>
<li>{this.randomDetails(5)}</li>
<li>{this.randomDetails(6)}</li>
</ul>
<div className="star-rating">
<img src={this.starRating(1)} alt="*" />
<img src={this.starRating(2)} alt="*" />
<img src={this.starRating(3)} alt="*" />
<img src={this.starRating(4)} alt="*" />
<img src={this.starRating(5)} alt="x" />
</div>
</div>
<div className="product-desc-checkout">
<div className="product-desc-total">
<h4>€ {this.preTaxCalculator().toFixed(2)}</h4>
<p>VAT - € {this.taxCalculator().toFixed(2)}</p>
<p>Shipping: € {this.props.product.shipping}</p>
<h3>{this.props.product.price}</h3>
</div>
<div className="product-desc-action-buttons">ADD TO CART</div>
<div className="product-desc-action-buttons">
ADD TO WISHLIST
</div>
<div className="product-desc-action-buttons">
SAVE FOR LATER
</div>
</div>
</div>
</div>
</div>
<div className="product-details">
<h2>Learn more about the {this.props.product.name}</h2>
<table>
<tbody>
{this.renderKeys().map(key => (
<tr key={key}>
<td>{key}</td>
<td>{this.props.product.details[key]}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>}
Answer:
const mapStateToProps = (state, ownProps) => {
const id = ownProps.match.params.product_id;
if (state.firestore.ordered.products) {
return {
product: state.firestore.ordered.products.find(p => p.id === id)
};
}
};
I used an if statement in the mapStateToProps.

Categories

Resources