I'm using the following component to display a list of images in React.
class Viewer extends React.Component{
constructor(props){
super(props);
this.state = {
images : ImageList
};
}
render(){
return (
<div className="row">
<div className="image-viewer">
<ul className="list-inline">
{this.state.images.map(function (image) {
return (<li key={image.mediaId}><a href="#"><img src={image.src}
className="img-responsive img-rounded img-thumbnail"
alt={image.mediaId}/></a></li>);
})}
</ul>
</div>
</div>
);
}
}
I want to trap the onClick event on an image to do some processing, like apply a CSS to put an overlay. How do I do this in React. Please understand I'm rank new to React.
If you update your component to have onClick={this.onImageClick.bind(this, image.mediaId) you can then handle the onClick plus know which image it was that was clicked, save that on state, and use it to apply a className to the image on render (if that's what it is that you want!).
So something like this:
{this.state.images.map(function (image) {
return (<li key={image.mediaId} className={this.state.activeImage === image.mediaId ? 'active' : undefined}><a href="#" onClick={this.onClick.bind(this, image.mediaId)}><img src={image.src} className="img-responsive img-rounded img-thumbnail" alt={image.mediaId}/></a></li>);
})}
And then onClick handler -
onClick: function (mediaId) {
this.setState({activeImage: mediaId});
}
Related
I am using a react component react-awesome-modal as follows.
<Modal visible={this.state.visible} width="850" height="450" effect="fadeInUp" onClickAway={() => this.closeModal()}>
Now I want to add a new style overflow-y:scroll to this Modal component.
I tried doing this, but it didn't worked:
<Modal visible={this.state.visible} style={{"overflow-y":"scroll"}} width="850" height="450" effect="fadeInUp" onClickAway={() => this.closeModal()}>
Is there any way I can apply the property to this component.
PS: I had tried applying this property using chrome Devtool(Inspect element). In that, I am able to see the complete CSS that is used for this component, so I was able to update it easily (see the last line in the image)
You could try insert a div element under <Modal /> , then set the style of this div:
<Modal visible={this.state.visible} width="400" height="300" effect="fadeInUp" onClickAway={() => this.closeModal()}>
<div style={{overflowY:"scroll", height:"300px"}}>
<h1>Title</h1>
<p>Some Contents</p>
<a href="javascript:void(0);" onClick={() => this.closeModal()}>Close</a>
</div>
</Modal>
If solution above doesn't work, then try creatRef, get DOM of <Modal />, and change the style of <Modal />: (React version must be newer than 16.3)
import React, { Component } from 'react';
import Modal from 'react-awesome-modal';
class Test extends React.Component {
constructor(props) {
super(props)
this.modalRef = React.createRef() //create Ref to get DOM
}
componentDidMount(){
this.modalRef.current.style.overflowY = "scroll" //change style of Modal DOM
}
render() {
return (
<div>
<Modal visible={this.state.visible} width="400" height="300" effect="fadeInUp" onClickAway={() => this.closeModal()} ref={this.modalRef}>
<div>
<h1>Title</h1>
<p>Some Contents</p>
<a href="javascript:void(0);" onClick={() => this.closeModal()}>Close</a>
</div>
</Modal>
</div>
)
}
}
I am trying to call my modal by changing the state from outside the react class. But so far no luck. I tried the following:
I have a method named Portfolio which needs to activate the modal once user click on the Image
const Portfolio = (props) =>
<div className="col-sm-4">
<div className="mb-2">
<img
onClick={this.toggle}
className="card-img-top"
src={"/assets/imgs/project/"+props.portfolio.img_thumb}
alt="" />
</div>
</div>
Here is the Class that contain the state and the modal. But I cannot change state to activate the modal.
class Project extends React.Component
{
constructor(props, context) {
super(props, context);
this.state = {
modal: false,
}
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
modal: !this.state.modal
});
}
render(){
return (
<div className="row portfolioWrap">
// here resides our modal which activate once modal state changed
<div>
<Modal
isOpen={this.state.modal}
toggle={this.toggle}
className={this.props.className}>
<ModalHeader toggle={this.toggle}>Modal title</ModalHeader>
<ModalBody>
{this.state.gallery.map(gallery =>
<img
className="card-img-top"
src={"/assets/imgs/project/"+gallery} alt="" />)
}
</ModalBody>
<ModalFooter></ModalFooter>
</Modal>
</div>
// calling portfolio method inside the loop
{currentTodos.map(item => <Portfolio key={item.title} portfolio={item} />)}
</div>
)
}
}
I am trying to activate the modal from Portfolio function. But since its outside the class scope i cannot access the state. Is there any way to accomplish this?
You can pass the toggle method to your Portfolio component then use it from the props.
<Portfolio key={item.title} toggle={this.toggle} portfolio={item} />
Then in Portfolio:
const Portfolio = (props) =>
<div className="col-sm-4">
<div className="mb-2">
<img
onClick={props.toggle}
className="card-img-top"
src={"/assets/imgs/project/"+props.portfolio.img_thumb}
alt="" />
</div>
</div>
I have made CARD's which display's username. When I click on the delete button i.e cross or cancel button it should remove the CARD's from the tasklist here tasklist is state variable. I am using .map() method to iterate over each task and display it. I want to delete the task card of a particular user when I click on the red cross button (see screenshot) currently only the window appears saying -> are you sure you want to delete it if I click yes it should delete it.
Code:
import React, {Component} from "react";
export default class Tasks extends Component{
constructor(props){
super(props);
this.state = {
taskList:[],
taskName:"",
type:"classification",
datasetName:"",
allDatasets:[],
users:[],
names:[]
}
}
triggerDelete(task){
if(window.confirm("Are you sure you want to delete this task?")){
}
}
render(){
return(
<div className="tasks-wrap">
<h1 onClick={()=>{
this.props.history.push("/taskdetails");
}}>Your Tasks</h1>
{
this.state.taskList.map((task,index)=>{
return(
<div key={index} className="item-card" onClick={()=>{
window.sessionStorage.setItem("task",JSON.stringify(task));
this.props.history.push("/taskdetails/");
}}>
<div className="name">{task.name}</div>
<div className="sub">
<div className="type">Dataset: {task.dateSetName}</div>
<div className="members">{task.userList.length + " participants"}</div>
</div>
<div className="del-wrap" onClick={(e)=>{
e.stopPropagation();
e.preventDefault();
this.triggerDelete(task);
}}>
<img src={require("../../images/cancel.svg")}/>
</div>
</div>
);
})
}
</div>
</div>
</div>
</div>
)
}
}
How should I modify my triggerDelete() method? So that the particular card gets deleted.
Pass index of the deleting task to the triggerDelete function and then just remove that index from this.state.taskList array.
<div className="del-wrap" onClick={(e)=>{
e.stopPropagation();
e.preventDefault();
this.triggerDelete(task, index);
}}>
<img src={require("../../images/cancel.svg")}/>
</div>
And in triggerDelete function
triggerDelete(task, index){
if(window.confirm("Are you sure you want to delete this task?")){
let taskList = [...this.state.taskList]
taskList.splice(index, 1);
this.setState({taskList: taskList})
}
}
you need to write the logic to delete the task, its easier to do it if you pass the index of the task to triggerDelete. window.confirm returns a boolean value indicating whether OK or Cancel was selected (true means OK).
import React, {Component} from "react";
export default class Tasks extends Component{
constructor(props){
super(props);
this.state = {
taskList:[],
taskName:"",
type:"classification",
datasetName:"",
allDatasets:[],
users:[],
names:[]
}
}
triggerDelete(index){
if(window.confirm("Are you sure you want to delete this task?")){
this.setState(prevState => ({
taskList: [...prevState.taskList.slice(0, index), ...prevState.taskList.slice(index + 1)]
}))
}
}
render(){
return(
<div className="tasks-wrap">
<h1 onClick={()=>{
this.props.history.push("/taskdetails");
}}>Your Tasks</h1>
{
this.state.taskList.map((task,index)=>{
return(
<div key={index} className="item-card" onClick={()=>{
window.sessionStorage.setItem("task",JSON.stringify(task));
this.props.history.push("/taskdetails/");
}}>
<div className="name">{task.name}</div>
<div className="sub">
<div className="type">Dataset: {task.dateSetName}</div>
<div className="members">{task.userList.length + " participants"}</div>
</div>
<div className="del-wrap" onClick={(e)=>{
e.stopPropagation();
e.preventDefault();
this.triggerDelete(index);
}}>
<img src={require("../../images/cancel.svg")}/>
</div>
</div>
);
})
}
</div>
</div>
</div>
</div>
)
}
}
On the parent component I am trying to pass down the state ( which are just images ) down to 2 child components. One of the child components just displays the images.
The other child component has an event handler to change one of the images.
Here is my code I am getting no errors yet the image is not changing.. Thanks!
I am not including imports to cut down on code!
Parent component:
export default class Parent extends React.Component{
constructor(){
super();
this.state = {
img1: <img id="img1" className="img-responsive center-block" name="img1" src="img1.png"/>,
img2: <img id="img2" className="img-responsive center-block" name="img2" src="" />
};
}
render(){
return (
<div>
<displayBox layers={this.state}/>
<eventsBox layers={this.state}/>
</div>
)
}
}
This is the display box component:
export default class AvatarCreationBox extends React.Component{
render(){
return (
<div>
{this.props.layers.img1}
{this.props.layers.img2}
</div>
)
}
}
Here is the other child component with the event handler:
It has this event handler that has been tested and works with a console.log at the end of it.
itemClicked(itemId,event){
event.stopPropagation();
if(itemId === "img3"){
this.props.layers.img1 = <img className="media-object" src="img3.png" alt="..."/>;
console.log("yo");
}
}
You need to define a function which manipulates the state in your parent class and pass it as prop. Next step is to call the function with new image inside your event class.
Change your Parent Class to this
export default class Parent extends React.Component{
constructor(){
super();
this.state = {
img1: <img id="img1" className="img-responsive center-block" name="img1" src="img1.png"/>,
img2: <img id="img2" className="img-responsive center-block" name="img2" src="" />
};
this.changeImage = this. changeImage.bind(this)
}
changeImage(newImage){
this.setState({img1: newImage});
}
render(){
return (
<div>
<displayBox layers={this.state}/>
<eventsBox
layers={this.state}
callback={this.changeImage}/>
</div>
)
}
}
and change event box code to following
itemClicked(itemId,event){
event.stopPropagation();
if(itemId === "img3"){
this.props.callback('<img className="media-object" src="img3.png" alt="..."/>');
console.log("yo");
}
}
Update: React components using ES6 classes no longer autobind this to non React methods. In your constructor add
this.changeImage = this. changeImage.bind(this) in the constructor.
I have:
JobScreen
handleSetView(mode, e) {
this.setState({
view: mode
});
console.log(this.state.view)
}
render() {
return (
<div className="jobs-screen">
<div className="col-xs-12 col-sm-10 job-list"><JobList view={this.state.view} /></div>
<div className="col-xs-12 col-sm-2 panel-container">
<div className="right-panel pull-right"><RightPanel handleSetView={this.handleSetView} /></div>
...
)
}
RightPanel
render() {
return (
<div>
<div className="controls">
<span className="title">Views <img src="images\ajax-loader-bar.gif" width="24" id="loader" className={this.state.loading ? "pull-right fadeIn" : "pull-right fadeOut"}/></span>
<button onClick={this.props.handleSetView.bind(this, 'expanded')}><img src="/images/icons/32px/libreoffice.png" /></button>
<button onClick={this.props.handleSetView.bind(this, 'condensed')}><img src="/images/icons/32px/stack.png" /></button>
</div>
...
)}
JobList
render() {
var jobs = [];
this.state.jobs.forEach((job) => {
jobs.push(
<Job key={job.id} job={job} view={this.props.view} loading={this.state.loading} toggleTraderModal={this.props.toggleTraderModal} toggleOFTModal={this.props.toggleOFTModal}/>
);
});
return (
<div>
{jobs}
</div>
);
};
The problem is, is that the changing of the view state does not rerender any of the child elements.
How can I get this to work?
Not sure if it is the core of your problem, but:
handleSetView={this.handleSetView}
is wrong, because how js binds this. Use:
handleSetView={this.handleSetView.bind(this)}
instead.
Also,
this.setState({
view: mode
});
console.log(this.state.view)
seems strange; note that this.state is not modified right after you call setState, it may took some time while React dispatches the scheduled setState operation. Put that console.log into render to see when it is actually called.
Finally, make sure, your components do not implement shouldComponentUpdate lifecycle method (you are probably not doing this explicitly, but if your component extends some class other than React.Component this may happen)
this is in the context of the react Component so either pass the reference of this to you function handleSetView or bind this as mentioned by #Tomas