I have an image gallery where I loop trough image objects and I want to pass the i to my onClick function. This is my image gallery code:
<div className="gallery clearfix">
{ block.gallery.map((item, i) => (
i < 1 ?
<div className="gallery-image" key={i} onClick={this.toggle}>
<a href='' className="inner">
<img src={item.images.thumbnail_sm} alt={block.title} srcSet={`${item.images.thumbnail_md} 1x, ${item.images.thumbnail_lg} 2x`} className="img-fluid image"/>
</a>
</div>
: null
))}
<div className="gallery-thumbs">
<div className="row">
{ block.gallery.map((item, i) => (
i > 0 && i < (limit + 1) ?
<div className="gallery-item" key={i} onClick={this.toggle}>
<a href='' className="inner">
<img src={item.images.thumbnail_sm} alt={block.title} srcSet={`${item.images.thumbnail_md} 1x, ${item.images.thumbnail_lg} 2x`} className="img-fluid image" title="" />
{ block.gallery.length > (limit + 1) && i == limit ?
<div className="img-overlay">
<span className="img-indicator">{ block.gallery.length - (limit + 1) }+ <span className="hidden-xs">Foto's</span></span>
</div>
: null
}
</a>
</div>
: null
))}
</div>
</div>
</div>
And this is my reactstrap modal where I want to show the image which is clicked:
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className}>
<ModalBody>
<img src={block.gallery[this.state.clickedImage].item.images.thumbnail_lg}/>
</ModalBody>
</Modal>
And here is the toggle function where I want to pass the clickedImage id:
toggle(id) {
this.setState({
clickedImage: id,
modal: !this.state.modal
});
}
For best practice, I don't suggest binding within onClick, that cause it invoke bind every time when it's clicked. if you are using ES6, instead you should bind it in constructor:
Class MyComponent extends React.Component {
constructor(props){
super(props);
this.toggle = this.toggle.bind(this);
}
}
and
<div className="gallery-item" key={i} onClick={(i) => this.toggle(i)}></div>
UPDATE: like comments say. this is actually is not the best way, the best way is not to create new function and attach events with every render, which means it should be just
<div className="gallery-item" key={i} onClick={this.toggle}></div>
but since you need to pass the id, the best bet would be refactor it into smaller components like <GalleryItem> and pass the id by props
Worth to read: this
UPDATE: Also please look at comments, using dataset.index and data-index is even better
Related
I'm using react and have a list with images, when hovered them it shows a text and when clicked it opens a modal, but each "li" has different content that goes in the modal. The problems is that every item of the list open only one modal, the last one, how can I do to open the correct modal?
Modal code
constructor(props) {
super(props);
this.state = {
visible : false
}
}
openModal() {
this.setState({
visible : true,
});
}
closeModal() {
this.setState({
visible : false,
});
}
list with the modal
<li className="bor">
<img src={bor}/>
<span className="first">teste</span>
<span className="second">veja o case completo</span>
<input type="button" onClick={() => this.openModal()} />
<section>
<Modal className="modal" visible={this.state.visible} width="90%" height="90%" effect="fadeInUp" onClickAway={() => this.closeModal()}>
<div className="close">
<a href="javascript:void(0);" onClick={() => this.closeModal()}>X</a>
</div>
<div>
<h1>teste1</h1>
</div>
</Modal>
</section>
</li>
<li className="baz">
<img src={baz}/>
<span className="first">teste2</span>
<span className="second">veja o case completo</span>
<input type="button" onClick={() => this.openModal()} />
<section>
<Modal className="modal" visible={this.state.visible} width="90%" height="90%" effect="fadeInUp" onClickAway={() => this.closeModal()}>
<div className="close">
<a href="javascript:void(0);" onClick={() => this.closeModal()}>X</a>
</div>
<div>
<h1>teste2</h1>
</div>
</Modal>
</section>
</li>
As #FatemehQasemkhani said it is best practice to use single modal & pass corresponding data like below. I approaced same way & it is working fine. I am taking some dummy data inside items. Whenever user is clicking on any list(li) item I am storing that currrent clicked item inside activeItemData and passing that value in modal.
import React, { Component } from "react";
import Modal from "react-awesome-modal";
class ProList extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
activeItemData: "",
items: [
{
desc: "this is item 1 "
},
{
desc: "this is item 2"
}
]
};
}
closeModal() {
this.setState({
visible: false
});
}
handleCurrentItem = item => {
// you can set two properties in setState or you can set visible property in callback also...
this.setState(
{
activeItemData: item.desc,
visible:true
},
// () => this.setState({ visible: true })
)
};
render() {
return (
<div>
<ul>
{ this.state.items.map(item => (
<li key={item.desc} onClick={() => this.handleCurrentItem(item)}>
{item.desc}
</li>
))}
</ul>
<Modal
className="modal"
visible={this.state.visible}
width="90%"
height="90%"
effect="fadeInUp"
onClickAway={() => this.closeModal()}
>
<div className="close">
<a href="javascript:void(0);" onClick={() => this.closeModal()}>
X
</a>
</div>
<div>
<h1>{this.state.activeItemData}</h1>
</div>
</Modal>
</div>
)
}
}
I want to show a list of messages which are in an array. In that list element contains a child element of "div". there need append more div in that child elements based on cases using the id of that particular div.
But i've getting null from both document.getElementById and React.createRef() methods. I've tried below sample source,
this.state.messageDetail.map((message, index) => {
return (
<React.Fragment key={index}>
{(index === 0 ||
this.isSameDate(
message.created_ts,
this.state.messageDetail[index - 1].created_ts
)) && (
<li className="date">
<span>{moment.unix(message.created_ts).format("MMM DD, YYYY")}</span>
</li>
)}
{(index === 0 ||
this.isSenderAndTimeSame(
message.sender_id,
message.created_ts,
index
)) && (
<li className="user" ref={this.userDetailRef}>
<React.Fragment>
<div className="img">
<img
alt=""
src={
message.sender_id === null
? CURRENT_USER_IMAGE
: BUCKET_PATH + "/" + this.state.currentTimeline.image
}
/>
</div>
<div id={"user_detail_" + index} className="details">
<div className="details-header">
<span className="userName">
{message.sender_id === null
? CURRENT_USER_NAME
: this.state.currentTimeline.name}
</span>
<span className="time">
{moment.unix(message.created_ts).format("hh:mm A")}
</span>
</div>
<div className="message">{message.message_body}</div>
</div>
</React.Fragment>
</li>
)}
</React.Fragment>
);
});
In the above sample isSenderAndTimeSame method will create and append a new child element by find div id (ie.user_detail_3). I am getting null value.
Kindly suggest your best way to do it.
I'm adding a UI image:
Using react, I have a simple console.log('hello') in my app.js. Im passing it by props to a component, "thumbnails group" that will map over an array, create "thumbnails", and each one of them should fire that method with onClick. When I do so, nothing happens. What am I missing ?
App.js
escolheTextura = () => {
console.log('hello')
};
<ThumbnailGroup
escolheTextura={this.escolheTextura}/>
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
onClick={() => props.escolheTextura()}>
{thumbnail}
</Thumbnail>
}
)
}
</div>
);
Thumbnail.js
return (
<div className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
};
You should pass props.escolheTextura even further. DOM element receives the click, not a React.js component So, you have to pass your function down to Thumbnail component and make some DOM element, saying div, to handle click event:
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
escolheTextura={props.escolheTextura}>
{thumbnail}
</Thumbnail>
}
)
}
</div>
);
ThumbnailGroup.js
return (
<div onClick={this.props.escolheTextura} className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
ThumbnailGroup.js
return (
<div className={["column", "group"].join(' ')}>
{
props.texturas.map(thumbnail => {
return <Thumbnail
escolheTextura ={props.escolheTextura}>
{thumbnail}
</Thumbnail>
}
}
</div>
);
Thumbnail.js
return (
<div onClick={props.escolheTextura} className={["button is-primary", "thumbnail"].join(' ')}>
<span>
<figure className={"image is-32x32"}>
<img src={"image is-32x32"}/>
</figure>
</span>
</div>
)
};
I've a list of photos being displayed as a React Component.
The individual list items are initially displayed with a + sign. The behavior I'm trying to acheive is on clicking a particular list item, the sign changes to -, and once a different list item is clicked, the first one reverts to + and the current one goes to -.
This is my render code,
render() {
let classes = "glyphicon add-icon " + (this.state.glyphClass ? "glyphicon-plus" : "glyphicon-minus");
return (
<div className="row">
<ul className="list-inline">
{this.props.images.map(function (image) {
return (<li key={image.id}>
<a href="#" onClick={this.getMediaId} data-id={image.id} data-class={image.src} data-owner={image.owner}>
<div className="img-wrapper">
<div className="img" style={{backgroundImage: `url(${image.src})`}}></div>
<div className="img-selector">
<span className={classes} id="plus-icon" aria-hidden="true"></span>
</div>
</div>
</a>
</li>);
}, this)}
</ul>
</div>
);
}
This is the constructor,
constructor(props){
super(props);
this.getMediaId = this.getMediaId.bind(this);
this.state = { glyphClass : true };
}
And this is the method that does the toggle,
getMediaId(event){
event.preventDefault();
this.setState({
glyphClass: !this.state.glyphClass
});
console.log(this.state.glyphClass);
....
}
The behavior that I'm getting now is that onClick on any list item all the list items are toggling to - and then on a subsequent click all are toggling to +. I'd really appreciate some help in fixing this.
You can have a selectedItem in the state instead.
constructor(props){
super(props);
this.getMediaId = this.getMediaId.bind(this);
this.state = { selectedItem : null };
}
Then in the get media set the id of selectedItem when clicked.
getMediaId(id){
this.setState({
selectedItem: id
});
}
Then you can check the id when rendering the list.
if the selectedItem has the same id of the list, render the - else render +.
render() {
return (
<div className="row">
<ul className="list-inline">
{this.props.images.map(function (image) {
const classes = this.state.selectedItem === image.id ? 'glyphicon add-icon glyphicon-minus' : 'glyphicon add-icon glyphicon-plus';
return (<li key={image.id}>
<a href="#" onClick={(event) => {event.preventDefault(); this.getMediaId(image.id); }} data-id={image.id} data-class={image.src} data-owner={image.owner}>
<div className="img-wrapper">
<div className="img" style={{backgroundImage: `url(${image.src})`}}></div>
<div className="img-selector">
<span className={classes} id="plus-icon" aria-hidden="true"></span>
</div>
</div>
</a>
</li>);
}, this)}
</ul>
</div>
);
}
My entire component is attached here:
https://gist.github.com/j42/392e63b275e1209ac269
The relevant render method from the component is below, for convenience:
render() {
let className = this.state.active ? 'active hidden-xs' : 'hidden-xs';
return (
<div id="git-flow" className={className}>
<div className="beanstalk-container">
<div className="beanstalk"></div>
<ul className="events">
<TransitionSpring
defaultValue={this.getDefaults()}
endValue={this.getEndValues()}>
{key => {
this.state.people.map(function(person, i) {
let deployFailed = (Math.round(Math.random()*1) <= 0.4),
headline = (deployFailed) ? 'PUSH BLOCKED' : 'PUSHED TO PRODUCTION',
className = (deployFailed) ? 'status negative' : 'status positive',
icon = (deployFailed) ? 'ion-android-close' : 'ion-android-checkmark-circle';
return (<li key={i}
className="person"
style={{
top: `-100px`,
opacity: `1`
}}>
<div className={className}>
<i className={icon}></i>
<div className="label-text">
<h4>{headline}</h4>
<h5><strong>Dec 15th, 2015</strong> — 5:00PM</h5>
</div>
<ul className="notifications">
<li className="notification">
<i className="ion-email-unread"></i>
<span>Group Leader Notified</span>
</li>
</ul>
</div>
<div className="image">
<img src={person.picture.medium} className="img-responsive" />
</div>
</li>);
});
}}
</TransitionSpring>
</ul>
</div>
</div>
);
}
The Problem:
This code runs without any errors, but only renders so far as ul.events. Within that is <noscript data-reactid=".2.0.1.0"></noscript> which leads me to believe React is encountering an error with the JS, and thus silently ignoring the bracketed content.
Everything looks normal to me though... I'm still a bit new to react, so is there something basic I'm overlooking? I just want to have a list of avatars that essentially fadeInDown within a TransitionSpring (so components can be added and removed)...
Thanks for the help!
use React Native Animated.
It's designed to work with React Native.