I'm trying to get to work a delete btn which is inside a div which is wrapped in react-router-dom tag. I want to be able to navigate to the established path when that div is clicked. But if the target is that delete btn, then it shouldn't navigate just execute the onClick inside the delete btn. Is this possible? I couldn't figure out on my own. Thanks for reading.
<ul className="m-entries">
{
letters.map(letter => {
return (
<li className="o-entry" key={ letter.id }>
<Link to={`/Letters/letter/${ letter.id }`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{ letter.title }</h2>
// This is the delete btn
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
displayConfirmDeleteWdw(e, letter.id)
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{ letter.synopsis }</p>
</div>
</Link>
</li>
)})
}
</ul>
It's generally considered bad UI/UX to place interactive DOM elements within other interactive DOM elements (in fact some elements are sometimes illegal when used like this), but there is a way to accomplish this.
The button's onClick event should not propagate up to the Link component. Call stopPropagation on the event to prevent it from bubbling up further.
<ul className="m-entries">
{letters.map(letter => {
return (
<li className="o-entry" key={letter.id}>
<Link to={`/Letters/letter/${letter.id}`}>
<div className="l-entry-container">
<div className="l-entry__header">
<h2 className="c-text__h2 c-letter-title">{letter.title}</h2>
<button
className="c-entry__btn c-entry__dlt-btn"
onClick={(e) => {
e.stopPropagation(); // <-- prevent event from bubbling up
displayConfirmDeleteWdw(e, letter.id);
}}
type="button"
>
<XDeleteBtn />
</button>
</div>
<p className="c-text__p c-letter__preview">{letter.synopsis}</p>
</div>
</Link>
</li>
)})
}
</ul>
Related
I am wondering if I could stop all spin to appear when only one delete button is click? Right now when I click on one of the delete buttons, all the spinners appear on all the delete buttons which should have been only the delete button that is performing the action. How should get this to work? Here is my code:
<ul class="list-group">
<h5>Schedules</h5>
{scheduleData.map(items => {
return (
<li key={items._id} class="list-group-item">
{moment(items.startdate).format('MMMM DD, YYYY')}{' '}
<span style={{marginRight: '0.2rem', marginLeft: '0.2rem'}}>
to
</span>
{moment(items.enddate).format('MMMM DD, YYYY')}{' '}
<span style={{float: 'right'}}>
<button
className="btn btn-primary"
onClick={event => deleteSchedule(event, items._id)}
>
{' '}
{loading && <i className="fa fa-refresh fa-spin"></i>}
Delete
</button>
</span>
</li>
);
})}
</ul>
Many thanks in advance and greatly appreciate any helps. thanks
Assuming that loading is a Boolean variable you set true in deleteSchedule, to achieve the result you have to create a loading parameter for every entry.
IMO the best approach would be to create an indipendent element in which you set loading with useState hook. You should pass deleteSchedule to this element and then when button is clicked it should call setLoading(true) and call deleteSchedule.
Other option would be to create a loading list and insert the id of the element which button was clicked in this list then loading would become loading.includes(items._id).
Or you could iterate scheduleData and add loading attribute to every element.
Here is the code:
state: {
button: "notclicked"
}
changeclass = () => {
this.setState({ button: "clicked" })
}
<ul>
<li className="linkactive">
<a class="vote-up-off" onClick={ this.changeclass } href="/">
<Icon className={ this.state.button } className=" fa fa-area-chart"/>
</a>
</li>
<li>
<a class="vote-up-off"href="/">
<Icon className="fa fa-bar-chart fa-2x"/>
</a>
</li>
<li>
<a class="vote-up-off" href="/>
<Icon className="fa fa-line-chart"/>
</a>
</li>
</ul>
I am able to change the class on clicking <li>, But on clicking another <li>, how to make prev class back to notclicked?
I want to show the user which tab he is currently on
What happen now with your changeClass() function is that every time you click on the link, the state.button is set to "clicked".
What you could do in this method is start by checking the value of button and set it to the other value.
eg:
changeclass=()=>{
if(this.state.button === "notclicked"){
this.setState({button:"clicked"})
}
else {
this.setState({button:"notclicked"})
}
}
Then, if you want each <li> to trigger your changeclass function, onClick must be set for each one of them.
Also, there are a few typos in your code:
onClicke must be onClick
in the last <a> tag, the string besides href is not closed (there is a missing ")
To summarize, this code should work as expected:
state:{
button:"notclicked"
}
changeclass = () => {
if(this.state.button === "notclicked"){
this.setState({button:"clicked"})
}
else {
this.setState({button:"notclicked"})
}
}
<ul>
<li className="linkactive"><a class="vote-up-off" onClick={this.changeclass} href="/"><Icon className={this.state.button} className=" fa fa-area-chart"/></a></li>
<li><a class="vote-up-off"href="/" onClick={this.changeclass}><Icon className="fa fa-bar-chart fa-2x"/></a></li>
<li><a class="vote-up-off" href="/" onClick={this.changeclass}><Icon className="fa fa-line-chart"/></a></li>
</ul>
you can try it like this
// lets assume initial state is false
state = {clicked : false}
later on your component, you make ternary if by doing it like this
changeClicked = ()=>{
// this setState will take what you have previously, and
// will change it to opposite value
this.setState(prev=>({clicked: !prev.clicked}))
}
//...
<a onClick={this.changeClicked}>
<Icon className={clicked ? "clicked":"notclicked"} />
</a>
my suggestion is if you have something opposite like clicked-notclicked, on-off, up-down, etc that inherently one will always be true when the opposite is false, use boolean.
it will help you down the road.
when the user scrolls the screen, I need to know the currently displayed div id to set the div key selected.
<button
type="button"
//here, I want to access current displayed div id
// I need to add some css class based on current displayed div id
className={`header_link ${
current_displayed_div_id === "Video" ? "selected" : "" // here I need to check
}`}
onClick={() => {
scrollTo(videoRef.current);
}}
>
Price
</button>
//here is my div using creatRef
<div
className="section"
height:
id="Video"
ref={videoRef} style={{ "auto" }}
>
<div>
<div className="nav-item">
<span className="nav-link">
<i className="icofont icofont-contacts"></i>
{translate("video")}
</span>
<div className="material-border"></div>
</div>
</div>
<div className="mt-4 text-center">
<div className="embed-responsive embed-responsive-16by9">
<iframe
src={this.props.datatab.video}
title="video"
>
</iframe>
</div>
</div>
</div>
when user scrolls the screen, I need to know the currently displayed div id to set the div key selected.
You could use the browser's native Intersection Observer API to track if an element is on screen and integrate this into your React component setup. Or you could use a React library like https://github.com/researchgate/react-intersection-observer or https://github.com/fkhadra/react-on-screen which should work out of the box.
I am trying to refactor from class based to functional. While doing so I will need to use hooks instead of setting this.state etc.. I am trying to get a FORM to open when i click a button. The button will also change from "add reply" to "submit comment" once the form opens. I am stumped. This is the best thing I could come up with... Doesnt work. in fact, it makes my "add reply" button completely disappear. Any thoughts on this? Here is the code that I have written. inside of the comment I am trying to return a component using ternary....
image of component as-is
import React, {useState} from 'react';
import FormOpen from './FormOpen';
const CommentCreated = (props) => {
const [resource, setResource] = useState([{visible: false, addReply: 'Add Reply'}]);
return (
<div className="ui threaded comments">
<h3 className="ui dividing header">Comments</h3>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src= {props.avatar} />
</a>
<div className="content">
<a href="/" className="author">{props.author}
</a>
<div className="metadata">
<span className="date">Today at 5:42PM</span>
</div>
<div className="text">{props.content}
</div>
<div className="actions">
{resource.visible ? <FormOpen /> : null}
<a className="reply" onClick={() => {setResource([{visible: true, addReply: 'Submit Comment'}]);}}>{resource.addReply}
</a>
<a className="save">Save</a>
<a className="hide">Hide</a>
</div>
</div>
</div>
</div>
);
};
export default CommentCreated;
You should replace the setResource(['Submit Comment', true]) with :
setResource([{
visible: true,
addReply: 'Submit Comment'
}])
To match the shape of your state.
EDIT:
Here is a working example based on your code.
As I can see from your example, your resource state doesn't need to be an array but simply an object.
Furthermore, as you are using hyperlinks <a />, you must use preventDefault() on your event being triggered if you don't want the page to refresh.
I have been messing around with a few conditional rendering methods but, I can't seem to find one that works.
<div className="tags">
{adv_event.types.map(type => (
<div className="tag" key={type.tid}>
<h5 className="body-color">Event Type:</h5>
<Link to={`/events/category/${type.slug}`} className="home-link track-click">{type.name}</Link>
</div>
))}
</div>
Right now <h5 className="body-color">Event Type:</h5> is repeated for every tag. Is there a way to show the title once without adding it before each tag?
Move it outside the loop?
<div className="tags">
{adv_event.types.length > 0 ? (<h5 className="body-color">Event Type:</h5>) : ''}
{adv_event.types.map(type => (
<div className="tag" key={type.tid}>
<Link to={`/events/category/${type.slug}`} className="home-link track-click">{type.name}</Link>
</div>
))}
</div>