next js mapping with-portals - javascript

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

Related

React Server-side rendering error due to mismatch between client and server

I am working on a React app (18.2.0 version to be exact), which uses server side rendering. On one of the pages, I am getting a following error:
Code of the component:
<div className="O111-color-module O111-color-module--multi container is-opened" suppressHydrationWarning={true}>
<div className="O111-color-module-filters container">
{colorsData.colorFilters.map((filter, index) => <div key={index} className="O111-color-module-card offset-383">
<h2 className="O111-color-module-filters__header">{props.header}</h2>
<BaseSelect handleChange={handleFilterChange} selectedValue={selectedFilterGroup} options={getColorGroupOptions(filter)} label={filter.label} />
</div>)}
</div>
<div className="card-list--swipe-stack-cards">
<div className={`card details ${mobileFullScreenDetails && 'O111-color-module--multi-details-block'}`}>
<div className="O111-color-module--multi-details has-value">
<div className="O111-color-module--multi-details-top">
<div className={`O111-color-module--multi-details-top is-${selectedColor.colorType}`}>
<div className="O111-color-module--multi-details-color"
style={{ backgroundImage: `url('${selectedColor.previewColorImage}')` }}>
<h3>{selectedColor.colorName}</h3> {/*<---This is where the error is being thrown*/}
</div>
<div className="O111-color-module--multi-details-info">
<ColorProperties {...props} color={selectedColor} />
</div>
<button className="modal__close js-modal__close" onClick={() => setMobileFulscreenDetails(false)} />
{selectedColor.matchingColors && selectedColor.matchingColors.length > 0 &&
<div className="O111-color-module--multi-details-matching-colors">
<h4>{props.matchingColorsLabel}</h4>
<div className="O111-color-module--multi-details-matching-colors-container">
{selectedColor.matchingColors.map((matchingColor, index) =>
<div key={index}
className={`is-${matchingColor.colorType}`}
onClick={() => handleColorSelection(matchingColor.id)}
style={{ backgroundImage: `url('${matchingColor.colorImage}')` }}>
<span> {matchingColor.colorCode} </span>
</div>
)}
</div>
</div>}
</div>
</div>
<div className="O111-color-module--multi-details-bottom">
{getColorSampleOrder()}
</div>
</div>
</div>
</div>
I have added the suppressHydrationWarning={true} prop to the most outer div in the component, but it didn't make the error go away.
Anybody knows what exactly is causing the problem and why is there mismatch between client and server? I am not using Next.js (most questions with SSR-related problems in React are in apps which are using it).

React - Prevent child re-rendering in map

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

i have two views in my react Js , i have a toggle button that will toggle both views that is list and grid . how to add this using states?

These are the both views List and Grid. How do i add functionality to the toggle button using States.
<div>
- List item
{ this.state.articles.map((element) =>{
return <div className="media m-3 p-2" style={{border:"1px solid black"}}>
<img className="mr-3 " src={element.thumb_url}style={{height:"50px", width:"50px"}} alt="Generic placeholder image"/>
<div className="media-body">
<h5 className="mt-0">{element.eventname}</h5>
<p>{element.venue.street}</p>
<p className="card-text"><small className="text-muted">on {element.start_time_display} </small></p>
View Details
</div>
</div>
<div className="container my-3">
// <h3 className="text-center" style={{ margin: "35px 0px" }}>{document.title = `Discover Best Of Online Events [${this.props.category}]`}</h3>
// When loading will true it shows sppinner
// {this.state.loading && <Spinner />}
// {this.state.articles.map((element) => console.log(element))}
// { under the className row all the elements are aligned one by one }
// <div className="row my-4">
// <div className="list-group">
// when iterating the results from API it needs to give unique key
// {this.state.articles.map((element) => {
// return <div className="col-md-3" key={element.event_id}>
// <Events imageUrl={element.thumb_url_large} title={element.eventname} eventUrl={element.event_url} date={element.start_time_display} city={element.venue.city} />
// </div>
// })}
// </div>
// </div>

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

Pass value to a modal in jsx map

I have a map that render few items. How can I pass params like name of the item, id of the item etc to the modal component?
render(){
return(
<div>
<Modal
isOpen={this.state.OpenDeleteModal}
confirmationTitle={`Delete item`}
confirmationCancel={'No'}
confirmationSuccess={'Yes'}
closeModal={this.closeModal}
successModal={this.successModal}
>
<p className="center">Are you sure you want to delete this item?</p>
</Modal>
<div className="wrapper">
{map(items, obj =>
<div key={obj._id} className="panel-body">
<div className="row">
<h2 className="title">{obj.name}</h2>
<a onClick={()=> this.setState({OpenDeleteModal:true})}>Delete</a>
</div>
</div>
)}
</div>
</div>
)
}
I only can think of put the obj._id in the tag as custom attribute and when user click on delete it change the state of the selectedItem, pass it through props.
One simple solution is to remember for which item you have opened the modal. Something like below. Set the selected item when you open the modal. During delete, fetch it from state and delete it.
render(){
return(
<div>
<Modal
isOpen={this.state.OpenDeleteModal}
confirmationTitle={`Delete item`}
confirmationCancel={'No'}
confirmationSuccess={'Yes'}
closeModal={this.closeModal}
successModal={this.successModal}
>
<p className="center">Are you sure you want to delete this item?</p>
</Modal>
<div className="wrapper">
{map(items, obj =>
<div key={obj._id} className="panel-body">
<div className="row">
<h2 className="title">{obj.name}</h2>
<a onClick={()=> this.setState({OpenDeleteModal:true, selectedItem: obj._id})}>Delete</a>
</div>
</div>
)}
</div>
</div>
)
}
UPDATE: Make sure to clear the selectedItem after you close the modal.
A modal can access the state of the react component, you can store the information of the selected link in state
storeInformation(item) {
this.setState({OpenDeleteModal:true,
selectedId: item.id
selectedName: item.name
})
}
render(){
return(
<div>
<Modal
isOpen={this.state.OpenDeleteModal}
confirmationTitle={`Delete item`}
confirmationCancel={'No'}
confirmationSuccess={'Yes'}
closeModal={this.closeModal}
successModal={this.successModal}
>
<p className="center">Are you sure you want to delete this item?</p>
<div>Name is: {this.state.selecteName}</div>
</Modal>
<div className="wrapper">
{map(items, obj =>
<div key={obj._id} className="panel-body">
<div className="row">
<h2 className="title">{obj.name}</h2>
<a onClick={()=> this.storeInformation(obj)}>Delete</a>
</div>
</div>
)}
</div>
</div>
)
}

Categories

Resources