react How to make onClick not work only on certain elements - reactjs

development environment
react.js
typescript
next.js
I don't want to trigger the onClick process when the span tag is pressed.
How should I implement it?
return (
<div className="padding-16 flex gap-5 flex-container" onClick={onClickUser}>
<img src={img} />
<div className="flex flex-direction gridgap-5">
<p>
{name}
</p>
<p>
{age}
</p>
</div>
<span className="hover" onClick={isShowCard}>
<img src="img/dot.svg" />
</span>
</div>
)

You can use stopPropagation() for span click event.
isShowCard = (event) => {
event.stopPropagation()
...
}
return (
<div className="padding-16 flex gap-5 flex-container" onClick={onClickUser}>
<img src={img} />
<div className="flex flex-direction gridgap-5">
<p>
{name}
</p>
<p>
{age}
</p>
</div>
<span className="hover" onClick={isShowCard}>
<img src="img/dot.svg" />
</span>
</div>
)

Have onClickUser check to see whether it has an ancestor with a class name of hover before continuing. For example, change:
const onClickUser = (e) => {
// code
};
to
const onClickUser = (e) => {
if (e.target.closest('.hover')) {
// do nothing
return;
}
// code
};

Related

How to open only one Modal with using Modal template that I created

I was able to use the map method to display my static data in each container. There's a Modal template I created in order to provide more object/values info but if I click the button it opens all the modals of each containers at the same time because Modal tag wasn't assigned with unique index number when I mapped through my static data. Big brothers, please look at my codes and teach me how I can open only one associated Modal.
I created function Modal (template) and import to function Projects
function Modal({closeModal}) {
return (
<div className="modal-main">
<div className='modal-card'>
<img src="" />
<div className='modal-info'>
<div className='modal-title'>
<p className="title">title</p>
</div>
<div className='modal-body'>
<p className="tech">tech</p>
<p className="description">description</p>
<p className="github">github</p>
<p className="website">website</p>
</div >
<Button>detail</Button>
<Button onClick={()=> closeModal(false)}>close</Button>
</div>
</div>
</div>
)
}
export default Modal
function Projects() {
const [openModal, setOpenModal] = useState(false);
const [oneProject, setOneProject]
= useState(list_of_projects)
return (
<div className="section-header text-center mt-5">
<h4>projects</h4>
<p>list of proejcts</p>
<div className="proj-container">
{oneProject.map((item, index)=> {
return(
<div className='proj-map mx-2 my-2'key={index} >
<button type='button' className="btn btn-link " onClick={() => setOpenModal(true)}>
<div className=" proj-card card">
<img className='proj-img' src={item.thumbnail} style={{ width:300, height:300 }}/>
<div className="proj-text card-text">
<h3 className='title'>{item.title}</h3>
<h5 className='description'>description</h5>
<br/>
<h4>+</h4>
</div>
</div>
</button>
{openModal && <**Modal** closeModal={setOpenModal} /> }
</div>
)
})}
</div>
</div>

How to open specific post dropdown menu without effecting the others?

I have data that I am looping through and I have a slight issue with a dropdown menu. Whenever I click the dropdown button for a single post all the other posts open. And I don't know to solve it.
Here is the React code
const Post = () {
const [open, setOpen] = useState(false)
return(
{posts.map((post) => {
return (
<div className="card" key={post.postId}>
<div className="card-header">
<div className="card-header__avatar">
<img
src="https://source.unsplash.com/user/erondu/40x40"
alt=""
/>
</div>
<div className="card-header__username">
<span>
<strong>
{post.firstname} {post.lastname}
</strong>
</span>
<br />
<span>
<small>{date}</small>
</span>
</div>
<div
className="card-header__moreBtn"
onClick={() => setOpen(!open)}
>
<img src={More} alt="" />
{open && ( ------> here is where the problem comes
<div class="dropdown-wrapper">
<ul class="dropdown-menu">
<li class="dropdown-menu__item">
<Link href="#d" onClick={deletePost}>Edit</Link>
</li>
<li class="dropdown-menu__item">
<Link className="dropdown-menu__item__link" onClick={deletePost}>Delete</Link>
</li>
</ul>
</div>
)}
</div>
</div>
<div className="postDescription">
<p>{post.postDescription}</p>
</div>
<div className="imgPost">
<img src={post.image} alt="" />
</div>
</div>
)
})}
)
}
Here is a picture
Any help will suffice and I appreciate your time.
You're using the same state value for all items in your loop, so naturally they are going to all follow the same directive.
I would recommend creating a PostItem component that maintains its own state. Something like this:
const PostItem = ({post}) => {
const [open, setOpen] = useState(false)
return (
<div className="card" key={post.postId}>
<div className="card-header">
<div className="card-header__avatar">
<img
src="https://source.unsplash.com/user/erondu/40x40"
alt=""
/>
</div>
<div className="card-header__username">
<span>
<strong>
{post.firstname} {post.lastname}
</strong>
</span>
<br />
<span>
<small>{date}</small>
</span>
</div>
<div
className="card-header__moreBtn"
onClick={() => setOpen(!open)}
>
<img src={More} alt="" />
{open && (
<div class="dropdown-wrapper">
<ul class="dropdown-menu">
<li class="dropdown-menu__item">
<Link href="#d" onClick={deletePost}>Edit</Link>
</li>
<li class="dropdown-menu__item">
<Link className="dropdown-menu__item__link" onClick={deletePost}>Delete</Link>
</li>
</ul>
</div>
)}
</div>
</div>
<div className="postDescription">
<p>{post.postDescription}</p>
</div>
<div className="imgPost">
<img src={post.image} alt="" />
</div>
</div>
)
})
}
const AllPosts = ({posts}) {
return(
{posts.map(post => <PostItem post={post} />
)
}
Now each PostItem has a separate, internal instance of open in its own state and will act independently from the others.

Rendering list of items with ReactJS

I've stumbled upon a trouble with rendering list items inside the component. What am I doing wrong and how to fix it?
This is what I've got in state
this.state = {
tasksState: false,
cupsState: false,
...
citizensData: [
{
diamsEarned: 5,
rubiesEarned: 6,
}, ...
]
}
Throphies are given as props
<Citizens tasksState={this.state.tasksState}
cupsState={this.state.cupsState}
trophies={this.state.citizensData} />
const Citizens = (props) => {
let {
tasksState,
cupsState,
trophies
} = {...props};
let CitizensList = [];
for (let i=0; i<20; i++) {
CitizensList.push(
<div className="Citizens__container--block">
<div className="box label-box">
<label className="label-ct" htmlFor="citiz">Citizen {i+1}:</label>
</div>
...
<div className="box diamond-box">
<i className="fas fa-gem icon icon-diamond"></i>
<p>{trophies[i].diamsEarned}</p>
</div>
<div className="box ruby-box">
<i className="fas fa-gem icon icon-ruby"></i>
<p>{trophies[i].rubiesEarned}</p>
</div>
);
}
return (
<div className="Citizens">
<div className="Citizens__container">
<CitizensList />
</div>
</div>
);
}
As a result, list ain't rendered at all
You can replace this line:
<CitizensList />
with this:
{[...Array(20)].map(i => (
<div className="Citizens__container--block" key={i}>
<div className="box label-box">
<label className="label-ct" htmlFor="citiz">Citizen {i+1}:</label>
</div>
...
<div className="box diamond-box">
<i className="fas fa-gem icon icon-diamond"></i>
<p>{trophies[i].diamsEarned}</p>
</div>
<div className="box ruby-box">
<i className="fas fa-gem icon icon-ruby"></i>
<p>{trophies[i].rubiesEarned}</p>
</div>
</div>
))}
Though I recommend creating a component for the citizen.
In react it better to use components, more components more flexibility. If I were to do the same task I would create another functional component for your
CitizensList
yet you can follow the below approach,
Here i = number of iteration and also you are missing a closing div.
const Citizens = (props) => {
let {
tasksState,
cupsState,
trophies
} = {...props};
let i = 20;
return (
<div className="Citizens">
<div className="Citizens__container">
{
Array.from(Array(i)).map((j) =>
<div className="Citizens__container--block">
<div className="box label-box">
<label className="label-ct" htmlFor="citiz">Citizen {j+1}:</label>
</div>
...
<div className="box diamond-box">
<i className="fas fa-gem icon icon-diamond"></i>
<p>{trophies[j].diamsEarned}</p>
</div>
<div className="box ruby-box">
<i className="fas fa-gem icon icon-ruby"></i>
<p>{trophies[j].rubiesEarned}</p>
</div>
</div>
))
}
</div>
</div>
);
}

How to open only one div inside map function in react show it cannot open other div element

this is my code part where i have used state and also setstate functin so I can toggle state but it is in map function so it is also opening other div i just wanted to know is it possible to open only one div using toggle function
this.state = {
show: false
}
commentInputHandler = () => {
console.log("working")
this.setState({
show: !this.state.show
})
}
<div className="col-lg-8 offset-lg-2 pb-5">
{this.state.posts.map(post => (
<div key={post._id} className="card mt-4">
<div className="card-body">
<div className="d-flex justify-content-between align-items-center">
<h4 className="card-title w-75">{post.title}</h4>
<div className="w-25 text-right">
<span onClick={() => this.deletePost(post._id)} className="badge badge-danger cp">Delete</span>
<span className="badge badge-primary cp ml-3">
<Link className="text-white" to={`/postupdate/${post._id}`}>Update</Link>
</span>
</div>
</div>
<p className="card-text">
{post.body}
</p>
</div>
<div className="card-footer">
<div className="d-flex justify-content-end">
<span onClick={this.commentInputHandler} className="badge badge-pill badge-primary mr-3 cp">Comment</span>
<span onClick={this.handleClickOpen} className="badge badge-pill badge-info cp">View comment</span>
</div>
{this.state.show ?
<div>
<form className="mt-3">
<input
type="text"
className="form-control"
placeholder="Add comment"
/>
<div className="d-flex justify-content-end">
<button className="btn btn-primary btn-sm mt-2">Comment</button>
</div>
</form>
</div> : null
}
</div>
</div>
))}
</div>
Right now show state holds for only one boolean, you might change it to an object that will hold each post show condition with _id as key (of course you can add it direct into posts state, just you didn't attach it to your code...)
this.state = {
//inital as empty object
show: {}
}
componentDidMount() {
// I update state through didMount, though it is not the only truth and depends on rest code either logic
let show = {}
this.state.posts.map(post => show[post._id] = false)
this.setState({show})
}
Now you can change commentInputHandler into something dynamic
<span onClick={()=>this.commentInputHandler(post._id)} className="badge badge-pill badge-primary mr-3 cp">Comment</span>
commentInputHandler = id => {
const { show } = this.state
this.setState({
show: {...show, [id]: !show.id}
})
}
and adapt your checker
{this.state.show[post._id] ? ...
Right now, you have a single state.show that is shared by all the elements generated by map. You could make state.show an array but there is a much better way to handle this.
You should create a separate component where you will put all the code inside your map function. Put state.show and commentInputHandler inside that new child component. Then each child component will be able to show/hide itself independently of the other ones
Parent
{this.state.posts.map(post => <PostComponent key={post.id} post={post}/>)}
PostComponent
this.state = { show: false }
commentInputHandler = () => {
...
render()
return (
<div className="card mt-4">
...

Get bootstrap modal close event in reactjs

I am using React 16 with Bootstrap 4.
I am using bootstrap modal to display some values. I need to reset these values whenever modal is closed.
For Modal I have created a separate component. I dont want to use React-Modal as I get all the functionality in the current modal.
I know in plain javascript it is achieved using below code:
$(".modal").on("hidden.bs.modal"){
//reset values here
};
But I dont know how this is achieved in ReactJS?
Below is my code for modal:
<div className="modal fade modal-flex" id="large-Modal-OneUser" tabIndex={-1} role="dialog">
<div className="modal-dialog modal-lg" role="document">
<div className="modal-content">
<div className="modal-header" style={{display: 'block'}}>
<div className="row">
<div className="col-md-6">
<h2 style={{fontWeight:600}}>{newTimelineData.length > 0 ? newTimelineData[0].candidateName : ""}</h2>
</div>
<div className="col-md-6">
<span style={{display: 'inline-flex',alignItems: 'center',float:'right'}}><h4 style={{paddingRight:20}}>{isNotEmpty(this.state.scheduledFor) ? moment(this.state.scheduledFor).format("DD-MMM-YYYY"):this.checkScheduledFor(newTimelineData)}</h4>
<h3 style={{paddingRight: 10}}>{isNotEmpty(this.state.probability) ? this.getProbabilityHTML() : this.checkProbability(newTimelineData)}</h3>
{/*<button type="button" className="close" data-dismiss="modal" aria-label="Close" style={{marginTop: 0,marginBottom: 10}}>
<span aria-hidden="true">×</span>
</button>*/}
</span>
</div>
</div>
</div>
<div className="modal-body">
<div className="col-md-12">
{/*<div className="card">*/}
{/*<div className="card-block">*/}
{/* Horizontal Timeline start */}
<div className="cd-horizontal-timeline">
<div className="timeline">
<div className="events-wrapper">
<div className="events" id="foo">
<ol>
{newTimelineData.map((item, index) => (
<li key={item.id}>
<a
href="#0" onClick={()=> this.setHeaders(item)}
data-date={moment(item.scheduledFor).format('DD/MM/YYYY')}
className={index === 0 ? 'selected' : null}>
<Moment unix format="DD MMM">
{item.scheduledFor / 1000}
</Moment>
</a>
</li>
))}
</ol>
<span className="filling-line" aria-hidden="true" />
</div>
{/* .events */}
</div>
{/* .events-wrapper */}
<ul className="cd-timeline-navigation">
<li>
<a href="#0" className="prev inactive">
Prev
</a>
</li>
<li>
<a href="#0" className="next">
Next
</a>
</li>
</ul>
{/* .cd-timeline-navigation */}
</div>
{/* .timeline */}
<div className="events-content">
<ol>
{newTimelineData.map((item, index) => (
<li
key={item.id}
className={index === 0 ? 'selected' : null}
data-date={moment(item.scheduledFor).format('DD/MM/YYYY')}>
<div className="row">
<div className="col-sm-8" style={{fontSize:'1rem',paddingLeft: 3,paddingRight:0}}><b>Job</b> : {item.jobName}</div>
{isNotEmpty(joiningDate) && <div className="col-sm-4" style={{fontSize:'1rem',paddingLeft: 3,paddingRight:0}}><b>Joined Date : </b>{joiningDate}</div>}
</div>
<div className="row">
<div className="col-sm-8" style={{fontSize:'1rem',padding:0}}><b>Stage</b> : {this.props.stage}</div>
{isNotEmpty(offerDate) && <div className="col-sm-4" style={{fontSize:'1rem',paddingLeft: 0,paddingRight:0}}><b>Offer Date : </b>{offerDate}</div>}
</div>
<br></br>
<div className="row">
<div className="col-sm-12" style={{fontSize:'1rem',paddingLeft: 0}}><b>Comments</b> :<br></br>{item.comments}</div>
</div>
</li>
))}
</ol>
</div>
{/* .events-content */}
</div>
{/* Horizontal Timeline end */}
{/*</div>*/}
{/*</div>*/}
</div>
</div>
<div className="modal-footer">
<button style={{backgroundColor: '#8080808f',borderColor:'#8080808f'}}
type="button"
className="btn btn-primary waves-effect waves-light"
data-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
Can anyone help?
See below snapshot for the work around I have tried suggested by #Jayavel.
Implemented a small workaround and hope it's fulfills your need, With my understanding you load your modal body with state and user will change something and you store those in the same state.
While closing the modal(close button) you need to reset the initial state i.e reset to default values.
Is that right !!! check this demo
what you need is,
store your default state like below:
const initialState = {
isOpen: false,
value: "defaultvalue"
};
and in component :
class App extends Component {
constructor(props) {
super(props);
this.state = initialState; // stored defaultstate
}
toggleModal = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
toggleModalClose = () => { // modal close to reset input val
this.setState(initialState);
}
handleChange = (e) => {
this.setState({
value: e.target.value //input new value
});
}
render() {
return (
<div className="App">
<button onClick={this.toggleModal}>
Open the modal
</button>
<Modal show={this.state.isOpen}
onClose={this.toggleModalClose}>
<input type="text" value={this.state.value} onChange={this.handleChange} />
</Modal>
</div>
);
}
}
Hope this helps.

Resources