Render dynamically two image carousels on the same page - javascript

Update
I have an array of objects with data and one of the key is gallery, which is an array of URLs: data=[{id:1,gallery:[]},{id:2, galery:[]}].
I did an image carousel in the mapped "data" array. The problem is that the carousels, of each item, are not independent one of each other. Each time I change the picture on one of them, it changes to the other also.
Here is the code:
export class MyComponent extends Component {
state = {
current: 0,
currentItem: '',
}
render() {
const selecItem = (e) => {
this.setState({
currentItem: e.target.parentElement.parentElement.parentElement.id
})
}
const resetSelectedItem = ()=>{
this.setState({
currentItem: ''
})
}
const nextSlide = (e ,arr) => {
if (this.state.currentItem !==
e.target.parentElement
.parentElement.parentElement.id ) {
e.preventDefault()
} else if (arr &&
this.state.currentItem === e.target.parentElement
.parentElement.parentElement.id ) {
let copy = this.state.current
this.setState({
current: (copy === arr.length - 1 ? 0 : copy + 1),
})
}
}
const prevSlide = (e, arr) => {
if (this.state.currentItem !== e.target.parentElement
.parentElement.parentElement.id ) {
e.preventDefault()
} else if (arr &&
this.state.currentItem === e.target.parentElement
.parentElement.parentElement.id ) {
let copy = this.state.current
this.setState({
current: (copy === 0 ? arr.length - 1 : copy - 1),
})
}
}
return (
<section>
{data &&
data.map((item, i) =>
<>
<div key={i} id={i.toString().concat(item.id)}>
<div>
...some data here
</div>
<div>
<div>
...more data
</div>
<div className='carousel' onMouseEnter={(e)=>selecItem(e)}
onMouseLeave={resetSelectedItem}>
<img src={left}
className='left-arrow' alt="left"
onClick={(e) =>this.state.currentItem
=== i.toString().concat(data[i].id)
? prevSlide(e, data[i].gallery) : undefined} />
<img src={right}
className='right-arrow' alt="right"
onClick={(e) => this.state.currentItem
=== i.toString().concat(data[i].id)
? nextSlide(e, data[i].gallery) : undefined} />
{data[i].gallery?.map((img, j) => (
<>
{j === this.state.current && (
<img src={img} alt={item.id} className='cart-image' key={j} />
)}
</>
))}
</div>
</div>
</div>
</>)}
</section>)}}
export default MyComponent
I want to have a different carousel for each item from data array. For the moment it is working like a big one, instead of two.
For me it is strange that all the other data it is rendered the way I want.
I tried this one also:
{this.state.currentItem === i.toString().concat(data[i].id)?
data[i].gallery?.map((img, j) => (
<>
{j === this.state.current && (
<img src={img} alt={item.id} className='cart-image' key={j} />
)}
</>
))}
In this case it is displayed and active, the carousel for only one item from data. But I want both carousels to be visible and active one at a time
Could you please help me?
Thank you in advance

You only check for the index of the item in the gallery as condition to show the image. If the selected gallery-item is 0 for example, every first item of every gallery will be shown. You will also need to save the active item-index in a state, together with the active gallery-item-index (as you already do) and add it to the condition to show the image.

I did a new component, and I moved all the logic for the image carousel inside of this component.
Now everything it is working fine.
The code it is looking something like this now:
export class MyComponent extends Component {
render() {
return(
<section>
{data &&
data.map(item=>
<NewComponent
id={item.id}
gallery={item.gallery} />
)}
</section>
)
}
}

Related

Set first element to show on load then hide when clicking on another React

I have a list of team members, each team member tile has an overlay, this is hidden when clicked on to show it's active, with a correlating text box to show information about them. I'm struggling to see how to make it so the first one will hide the overlay and show the content on load then when you make another selection it will add in the overlay and hide the content.
Currently I have it so when you click on a tile it will hide the overlay and show the content based on the index, and I'm trying to use the useEffect hook to target the first index and give it a true value but I can't set it up to display the first one without showing it constantly or breaking the logic altogether.
My function is below:
function KeyTeamMembers(props) {
const [isVisible, setIsVisible] = useState(true);
const toggle = i => {
if (isVisible == i) {
return setIsVisible(null)
}
setIsVisible(i)
}
useEffect((i) => {
i === 0 ? setIsVisible(true) : setIsVisible(false)
}, [])
return (
<KeyTeamMembersContainer>
<Container width={14}>
<Hello><span>2.4</span>Key Team Members</Hello>
<FlexInline>
<HalfWidth>
<FlexWrap>
{props.team.map((person, i) => (
<>
<TeamCardSmall key={slugify(person.firstName)} index={i}>
<TeamCardContent onClick={() => toggle(i)}>
<ThumbnailCover className={isVisible === i ? 'hide-cover' : 'cover' }>
<p>{person.firstName} {person.lastName}</p>
</ThumbnailCover>
<img src={person.image.fluid.src} alt="Test Images" className="card-item" />
</TeamCardContent>
</TeamCardSmall>
</>
))}
</FlexWrap>
</HalfWidth>
<HalfWidth>
{props.team.map((person, i) => (
<>
<TeamCardInfo className={isVisible === i ? 'accordionContent show' : 'accordionContent' } key={slugify(person.lastName)} index={i}>
<TeamCardBio>
<h3>{person.firstName} {person.lastName}</h3>
<BioRole>{person.role}</BioRole>
<p>{person.bio.bio}</p>
</TeamCardBio>
</TeamCardInfo>
</>
))}
</HalfWidth>
</FlexInline>
</Container>
</KeyTeamMembersContainer>
);
}
Try this method:
function KeyTeamMembers(props) {
const [isVisible, setIsVisible] = useState(null);
const toggle = (i) => {
if (isVisible == i)
setIsVisible(null)
else
setIsVisible(i)
}
return (
<KeyTeamMembersContainer>
<Container width={14}>
<Hello><span>2.4</span>Key Team Members</Hello>
<FlexInline>
<HalfWidth>
<FlexWrap>
{props.team.map((person, i) => (
<>
<TeamCardSmall key={slugify(person.firstName)} index={i}>
<TeamCardContent onClick={() => toggle(i)}>
<ThumbnailCover className={isVisible === i ? 'hide-cover' : 'cover' }>
<p>{person.firstName} {person.lastName}</p>
</ThumbnailCover>
<img src={person.image.fluid.src} alt="Test Images" className="card-item" />
</TeamCardContent>
</TeamCardSmall>
</>
))}
</FlexWrap>
</HalfWidth>
<HalfWidth>
{props.team.map((person, i) => (
<>
<TeamCardInfo className={isVisible === i ? 'accordionContent show' : 'accordionContent' } key={slugify(person.lastName)} index={i}>
<TeamCardBio>
<h3>{person.firstName} {person.lastName}</h3>
<BioRole>{person.role}</BioRole>
<p>{person.bio.bio}</p>
</TeamCardBio>
</TeamCardInfo>
</>
))}
</HalfWidth>
</FlexInline>
</Container>
</KeyTeamMembersContainer>
);
}

How to run loader icon on deleted row rendered from a list in react?

Hi i am rendering a list of photos from api response. When i delete on one of them, only that row should show the loader. But as of now i see the loader icon on all rows
let boilerImages;
if( listOfPhotos.data!== undefined && listOfPhotos.data.task && listOfPhotos.data.task.metadata !==''){
boilerImages =
listOfPhotos.data &&
JSON.parse(listOfPhotos.data.task.metadata).files.map((token, index) => {
console.log('key', index)
const fileUrl = `url/${token}?subscription-key="something"`
return (
<div className="list-wrapper" key={token+index}>
<div className="img-block">
<img className="img-block-bucket" src={fileUrl}/>
/* This is the part of loader - start */
{
this.props.isDeleting ?
<SvgIcon
svg={"icons/loading-spinner.svg"}
key={index}
fromUrl={true}
className={"deleting-svg"}
/>:
<label className="img-block-file-name">
{this.getFileName(fileUrl)}
</label>
}
/* This is the part of loader - end */
</div>
<button disabled={this.props.isDeleting} className="del-block" onClick={() => removePhoto(token)}>
<SvgIcon
svg={"icons/ic_trash_bin_48_black.svg"}
key={index}
fromUrl={true}
className={!this.props.isDeleting ? "del-block-svg" : "del-block--disabled"}
/>
</button>
</div>
)});
}
this.props.isDeleting is set to true in redux state, when i call removePhoto action and this.props.isDeleting is set to false in redux state, when i call renderPhoto action on update
Instead of isDeleting being a boolean, it will need to be an array of booleans where each entry is the loading state of the list item, then you just need to do something like
{(this.props.isDeleting[index] && ) && (
<SvgIcon
svg={"icons/loading-spinner.svg"}
key={index}
fromUrl={true}
className={"deleting-svg"}
/>)}
Thanks to #Bill. So instead of setting true or false at redux store. I tried having an object. So i used the token passed in my action - removePhoto
When i called removePhoto action
this.props.isDeleting: {
...state.data,
key: action.payload, //key: token
status: true
}
When i called renderPhoto action
this.props.isDeleting: {
...state.data,
key: null,
status: false
}
so the component has code something like this:
let boilerImages;
if( listOfPhotos.data!== undefined && listOfPhotos.data.task && listOfPhotos.data.task.metadata !==''){
boilerImages =
listOfPhotos.data &&
JSON.parse(listOfPhotos.data.task.metadata).files.map((token, index) => {
const fileUrl = `url/${token}?subscription-key="something"`
return (
<div className="list-wrapper" key={index}>
<div className="img-block">
<img className="img-block-bucket" src={fileUrl} onError={() => this.updateCRM(token)}/>
/* This is the part of loader - start */
{
this.props.isDeleting.key === token ?
<SvgIcon
svg={"icons/loading-spinner.svg"}
key={index}
fromUrl={true}
className={"delete-loader"}
/> :
<label className="img-block-file-name">
{this.getFileName(fileUrl)}
</label>
}
/* This is the part of loader - end */
</div>
<button disabled={this.props.isDeleting.status} className="del-block" onClick={() => removePhoto(token)}>
<SvgIcon
svg={"icons/ic_trash_bin_48_black.svg"}
key={index}
fromUrl={true}
className={!this.props.isDeleting.status ? "del-block--enabled" : "del-block--disabled"}
/>
</button>
</div>
)});
}

How to enable/disable elements from an array in React JS?

I am trying to create a component where I have a bunch of boxes from an array, that can be turned 'on' and 'off' when each one is individually clicked.
Currently, only a single item from the array can be switched 'on' (shown by the item turning green), however, I would like to be able to turn each item on/off individually.
Interacting with one element should not affect any of the others.
How do I achieve this?
My click event:
handleOnClick = (val, i) => {
this.setState({active: i}, () => console.log(this.state.active, 'active'))
}
Rendering the boxes:
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<button
key={i}
style={{...style.box, background: i === this.state.active ? 'green' : ''}}
onClick={() => this.handleOnClick(val, i)}
>
{val}
</button>
)
})
return options
}
Here's a Codepen
What I would do is to create a Box component with its own active state, and pass this to the map in renderBoxes. The benefit of doing it this way is that each Box component will have its own state independent of the parent. That way you can have more than one component as active.
so...
class Box extends React.Component {
constructor(props){
super(props)
this.state={
active: false
}
}
clickHandler = () => {
this.setState({active: !this.state.active})
}
render(){
const { key, children }= this.props
return (
<button
key={key}
style={{...style.box, background: this.state.active ? 'green' : ''}}
onClick={() => this.clickHandler()}
>
{children}
</button>
)
}
}
then have renderBoxes be...
renderBoxes = () => {
const options = this.state.needsOptions.map((val, i) => {
return (
<Box
key={i}
>
{val}
</Box>
)
})
return options
}
here is the codepen I forked off yours.

Pass index as state to component using React

I have 4 different divs each containing their own button. When clicking on a button the div calls a function and currently sets the state to show a modal. Problem I am running into is passing in the index of the button clicked.
In the code below I need to be able to say "image0" or "image1" depending on the index of the button I am clicking
JS:
handleSort(value) {
console.log(value);
this.setState(prevState => ({ childVisible: !prevState.childVisible }));
}
const Features = Array(4).fill("").map((a, p) => {
return (
<button key={ p } onClick={ () => this.handleSort(p) }></button>
)
});
{ posts.map(({ node: post }) => (
this.state.childVisible ? <Modal key={ post.id } data={ post.frontmatter.main.image1.image } /> : null
))
}
I would suggest:
saving the button index into state and then
using a dynamic key (e.g. object['dynamic' + 'key']) to pick the correct key out of post.frontmatter.main.image1.image
-
class TheButtons extends React.Component {
handleSort(value) {
this.setState({selectedIndex: value, /* add your other state here too! */});
}
render() {
return (
<div className="root">
<div className="buttons">
Array(4).fill("").map((_, i) => <button key={i} onClick={() => handleSort(i)} />)
</div>
<div>
posts.map(({ node: post }) => (this.state.childVisible
? <Modal
key={ post.id }
data={ post.frontmatter.main.[`image${this.state.selectedIndex}`].image }
/>
: null
))
</div>
</div>
);
}
}
This is a good answer which explains "Dynamically access object property using variable": https://stackoverflow.com/a/4244912/5776910

Render component inside .map onClick() - React

I'm trying to render component onClick and the problem I faced is that my onClick opens component in every row not only in the one I clicked button.
It's probably any index issue. Could you take a look at my code? Thanks!
class ItemView extends Component {
constructor(props) {
super(props);
this.state = {
isFormOpen: false,
parentId: null,
}
}
handleAddItem = (value) => {
this.setState({
parentId: value.parentId,
isFormOpen: !this.state.isFormOpen,
})
}
render() {
return (
<div className="ui relaxed divided list">
{data.items.map((item, index) => {
return (
<div className="item" key={index}>
<Button
label={'Add Item'}
onClick={() => this.handleAddItem({ parentId: item.parentId })}
/>
{isFormOpen && (
<AddItem parentId={parentId} />
)}
</div>
)
})}
</div>
)
}
}
add check if parentId is equal to item.parentId:
{isFormOpen && parentId === item.parentId && (
<AddItem parentId={parentId} />
)}
You are right.
For this you have to use Other Component. In this component because you are using map to render the element so it renders it in every time or row.
That's why when isFormOpen will be true so it'll be true on all items/rows.
Please use other component to render the conditional element.

Categories