Toggling CSS as a state for li items in React - javascript

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

Related

How to open a different modal that is inside of a list?

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

how to toggle active class in 2 elements react

im trying to toggle active class when you click on a card , for some reason its only working for the first div i have and not working for the second div element. basicly it should remove the box class and add active class when you click on the div element , however when i click on the second div it affects the style of the first div element instead. ANy help is greatly appreciated!
class RegisterForm extends React.Component {
constructor(props) {
super(props);
this.handleClick= this.handleClick.bind(this);
this.handleClick2= this.handleClick.bind(this);
this.state = {
active: false,
active2: false
};
}
handleClick(){
this.setState({ active: !this.state.active });
console.log(this.state.active);
}
handleClick2(){
this.setState({ active2: !this.state.active2 });
console.log(this.state.active2);
}
render() {
const { form } = this.props
return (
<div>
{' '}
<h1 id={styles.n}> Get started Today </h1>
<h5 id="f">No credit card required </h5>
<br />
<br />
<div className="container">
<div className={this.state.active ? styles.active : styles.box} onClick={this.handleClick} onKeyDown={this.handleClick} role="presentation">
<span className={styles.cont}>
<i className="fa fa-rocket" />.
</span>
<h3>Organization</h3>
<p className={styles.expand}>
<span className="plus">full access to all settings</span>
<span className="minus">-</span>
</p>
</div>
<div className={this.state.active2 ? styles.active : styles.box} onClick={this.handleClick2}>
<span className={styles.cont}>
<i className="fa fa-edit" />.
</span>
}
You are binding the this.handleClick function to this.handleClick2 in the constructor.

ReactJS Pass key with an onClick function

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

attach onClick handler to populated list item reactjs

i have a function that i want to fire when a button is being clicked, this button is included in a list item that gets populated based on values from an array passed from the parent component, but its not working and returning an error saying that the function is undefined, how can i make this work?
import React from 'react';
export default class Card extends React.Component {
constructor(){
super()
this.state = {
index: null
};
}
handleClick(x){
this.setState({
index: x
})
}
render(){
/**
* Populates list items according to data passed
* on to resultsArray.
*/
var items = this.props.resultsArray;
var itemslist = items.map(function(item, index){
return(
<li key={ index } class="card" >
<div class="card-header">
<span class="hour-price"><span>{ item.hourPrice } € /hour</span></span>
<img src={ item.image } class="card-img" />
Book
</div>
<div>
<div class="card-info">
<p class="workplace-name">{ item.name }</p>
<span class="score">{ item.score } ★</span>
<p class="location">{ item.location }</p>
</div>
<div class="card-footer">
<p class="price">{ item.price } € / Day</p>
</div>
</div>
</li>
);})
return(
<div class="results-container">
<ul class="card-list center">
{ itemslist }
</ul>
</div>
);
}
}
You should change your render to the below codes. Use that instead of this.
var items = this.props.resultsArray;
var that = this;
var itemslist = items.map(function(item, index){
return(
<li key={ index } className="card" >
<div className="card-header">
<span className="hour-price"><span>{ item.hourPrice } € /hour</span></span>
<img src={ item.image } className="card-img" />
Book
</div>
......

React.js modify child element on parent click

I have a following react component:
<li key={contact.id} class="option contact-item">
<div class="thumbnail">
<a><span>{contact.name.slice(0, 1)}</span></a>
</div>
<div class="text">
<div class="label">
<div class="name">{contact.name}</div>
<div class="status">{contact.status}</div>
</div>
<div class="select-container">
<div class="select">
<i class="icon-check"></i>
</div>
</div>
</div>
</li>
I need to toggle the color of <i class="icon-check"></i> when clicking the whole <li>
How can I do that?
Firstly, in react, you don't use class, you use className.
See this for full code: https://codepen.io/pen?editors=0010
Using state, you can change styles/classes/etc
_handleClick(key) {
let clicked = this.state.myList.filter(f => f.id === key)[0];
this.setState({ clicked: clicked });
}
_changeColor(key) {
if (this.state.clicked.id === key) {
return 'icon-checked';
}
else
return 'icon-check';
}
_renderList() {
return this.state.myList.map(contact => (
<li key={contact.id}
onClick={() => this._handleClick(contact.id)}
className="option contact-item">
<i className={this._changeColor(contact.id)}></i>
{contact.name}
</li>
));
}
render() {
return (
<div>
<h1>Hello</h1>
<ul>
{this._renderList()}
</ul>
</div>
);
}
You can set "changeColor" state on <li> click and your <i> can handle this state. If you change component state React will rerender your component.
<li onClick={this.changeColor()} key={contact.id} className="option contact-item">
<div className="thumbnail">
<a><span>{contact.name.slice(0, 1)}</span></a>
</div>
<div className="text">
<div className="label">
<div className="name">{contact.name}</div>
<div className="status">{contact.status}</div>
</div>
<div className="select-container">
<div className="select">
<i className="icon-check" style={if (this.state.colorChanged) {color: 'red'}}></i>
</div>
</div>
</div>
changeColor() {
this.setState({colorChanged: true});
}
I made a simple example for you, it works with a sinle list item Check this out.
If you have a list of items, you need to add a one more component for List itself with onToggleListItem method, which will handle the state change. The state of all list items should be store there. In ListItem component you call the onToggleListItem method with a contact id so you can identify which list item was changed.
handleButtonClick() {
this.props.onToggleListItem(this.props.contact.id);
}
Make use of a state and change the state on click of li
var Hello = React.createClass({
getInitialState() {
return {
colorClass: ''
}
},
toggleClass() {
if(this.state.colorClass == '') {
this.setState({colorClass: 'someColor'});
} else {
this.setState({colorClass: ''});
}
},
render(){
return (
<div>
<li onClick={this.toggleClass}>
Some text
<i className={"icon-check " + this.state.colorClass}>value</i>
</li>
</div>
);
}
})
ReactDOM.render(<Hello />, document.getElementById('app'));
.someColor {
color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app"></div>

Categories