How to access child's onClick in React.Fragment - javascript

I am very new to react and JavaScript and this is my second week of react learning. I am now stuck with the event propagation problem. I have tried to access onClick in the child of <React.Fragment> but event target is seem to be in <React.Fragment> itself. I can not find the simple solution. Please help, Thank you very much in advance.
return (
{loading ? ("...loading...") : data.map(({id,project_title,project_subtitle, project_description,image_url,link},index) => (
<React.Fragment>
<ProjectListButton onClick={(e)=>{ alert(index)}} id={"b"+id} project_title={project_title} project_subtitle={project_subtitle} />
<ProjectPlate project_description = {project_description} image_url ={image_url} link ={link} />
</React.Fragment>
)
)
}
)

<React.Fragment>
is wrapper, to wrap the html elements or sibling elements. It acts as a sudo parent and it just wraps them.
We can't add any attributes to the react fragment.
If you want to capture events, replace fragments with div and add a listener on it. It is not required to use fragment. It is just an option to save an extra element, if we just need them for wrapping purpose.

Related

Set custom html attribute to child element of material ui component

I have to add the custom HTML5 attribute "data-metrics" to the span element, but I'm not sure how to achieve that using the ListItemText Material UI component.
Component API Documentation: https://mui.com/material-ui/api/list-item-text/
This is my code at the moment:
<li>
<ListItem button key={`list_item_${index}`}
component={Link} to={data.url}
activeClassName={classes.activeLink} partiallyActive={true}
focusVisibleClassName={classes.buttonOnFocus}>
<ListItemText primary={data.name}
data-metrics={`{"btnname":"${data.name.toLowerCase()}"}`}/>
</ListItem>
</li>
And this is what it is generating:
Attribute is being set to div element instead of span(child)
Note the data-metrics attribute is being set to the div element instead of the span.
I think you need to use the "primaryTypographyProps" prop inside the component. At least I think that's what the docs are saying.
I think you need to pass it an object like so:
<ListItemText primaryTypographyProps={{"data-metrics": "test"}} />
You can use useRef hook for this purpose, after you found an element in your ref.current object you can invoke ref.current.setAttribute("data-metrics", VALUE)
It forwards ref to a root element

querySelector a variable class name in react

I have some external data fetched to some buttons , and I want when I click on a button to auto scroll to it section.
this is what I did :
const Target = (e) =>
document.querySelector("."+ e.target.className).scrollIntoView({
behavior: 'smooth'
});
}
{sideBarData.article.document.data.sections.map((sec)=>
(
<button className={sec.section.document.data.section_title.text} onClick={Target}>{sec.section.document.data.section_title.text == "" ? "Intro" :sec.section.document.data.section_title.text}</button>
)
)}
Eventually when I click on a button I get an error of querySelector is empty !
Use useRef against querySelector. The querySelector works in the real DOM. The useRef creates references to the virtual DOM, and these reference the manifestation realized during rendering.
Some links here:
W3School with example
Official react documentation

React Component's inline style is not working

I created Carousel component, it returns a collection of carousel boxes, before returning it I am applying style property to every returning div, but the style is not working. What should I do to fix it?
If I created another div inside the main div and wrap the content of outer div into inner div, and apply style property to inner div
instead to outer div then everything is working.
const Carousel = (props)=>{
return (
<Slider {...settings}>
{ props.sectionDetails?
props.sectionDetails.map(
(TypeBox)=>{
return (<div key={TypeBox.id} style={{background:
TypeBox.background_color}}>
<h3>{TypeBox.title}</h3>
<p>{TypeBox.description}</p>
</div>
);
}):"" }
</Slider>
);
}
I want only one div and style should work with this, I don't want to create another nested div.
Inline styles in React is a similar with JSON.
The style element must be writen together with camelCase.
The Value of the style element must be wraped in quotes.
I don't know will it work with you'r code, but try this anyway, this is the right way to call inline style:
style={{backgroundColor: "TypeBox.background_color"}}
Or You can try to wrap Your style into backticks like this:
style={{backgroundColor: `${TypeBox.background_color}`}}
The correct solution is style={{ backgroundColor: TypeBox.background_color }}.

Adding "manually" multiple React components in the same container

I'm migrating an old "jQuery" application, adding some React components. I have some JS/jQuery code adding some elements in the DOM. I want to replace this using for example a new Item component, creating many instances and adding them to the same container. And I need to get the real DOM element to manipulate it (with the old JS/jQuery code).
I found this solution :
const elt1 = ReactDOM.findDOMNode(ReactDOM.render(<CalendarItem item={item} />, container))
but the container content is replaced with the new Item and adding many items, only the last is finally in the container.
I have tried portal :
const elt2 = ReactDOM.createPortal(<CalendarItem item={item} />, container)
but the returned element elt2 is not a DOM element (that I can manipulate after).
Is there a solution to do this ?
Thanks
If you want to manipulate React components using their related DOM elements, look into Refs (https://reactjs.org/docs/refs-and-the-dom.html).
So create the ref in the constructor:
constructor(props) {
super(props);
...
this.calendarItem = React.createRef();
}
And then give the ref to your new CalendarItem:
<CalendarItem item={item} ref={this.calendarItem} />
You can then access the actual DOM node of that CalendarItem using this.calendarItem.current, for example:
$(this.calendarItem.current).focus();

Passing only clicked element to onClick function - reactjs

I have multiple elements with same className and i want if some element (with className history-node) is clicked it should get className active along with current className.
But i am having an issue, there are childs of that element and if child elements gets clicked they also get class Active.
Here is the code:
<div className="history-node-container" key={index}>
<div className="history-node" onClick={(e) => {this.handleHistoryClick(e.target)}}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
handleHistoryClick function
handleHistoryClick(target){
$('.history-node').removeClass('active'); //removing active class from all elements
target.className = 'history-node active';
}
I want to run function when user click on element with className history-node
But if user clicks on history-title, ClickHandler gives class active to history-title element.
EXPECTED BEHAVIOUR: if history-node is clicked only history-node should get class active.
One tip and how to solve your problem (In two ways)
Tip: Not usually the best idea to mix React with jQuery. React came in as a major paradigm shift in how we interact with the DOM. Try to read a little bit more about React, how it works, why is it so different from simply adding/removing elements in the DOM with jQ.
Some references to help you with that:
How to go from jQuery to React.js?
Thinking in React for jQuery Programmers
Now, back to your question
You should use currentTarget.
As the .history-title and .history-date elements are wrapped within .history-node, any click on them will trigger it's parent's event, since .history-node body is .history-title + .history-date. That's the correct behavior. When you trigger an event in JS, the event object receives two parameters: target, which is the event triggering the event and currentTarget, the element that is actually listening for the event.
Now for the code:
with JQ
Component:
<div className="history-node-container" key={index}>
<div className="history-node" onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
Click:
handleHistoryClick(event){
$('.history-node').removeClass('active')
event.currentTarget.classList.add('active')
}
The React way (Pure React, no modules)
Component:
class HistoryNode extends Component {
constructor(props) {
super(props)
this.state = { isActive: false }
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
let state = this.state
this.setState({isActive: !state.isActive})
}
render() {
return(
<div className="history-node-container">
<div className={`history-node ${this.state.isActive ? 'active' : ''}`} onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">
{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
)
}
}
Notice how you don't need to manipulate the DOM at any moment at the React solution. You just attach your event to that particular component, define how it's state change should reflect on the UI and let React do the rest.
Hope it helps ;)
Reference for target vs currentTarget
I think the event propagates to child components.
Have you tried this ?
<div className="history-node-container" key={index}>
<div className="history-node" onClick={handleHistoryClick}>
<span className="history-title">{heading.action}</span>
<span className="history-date">{moment(heading.added_at).format("MMMM Do, YYYY")}</span>
</div>
</div>
HandleClick function
handleHistoryClick(event){
event.stopPropagation();
$('.history-node').removeClass('active');
event.target.className = 'history-node active';
}
EDIT : You could make it simpler though (and without jQuery) using your component state. But without knowing how you wrote your component I cannot give you a snippet illustrating it. Be careful too as you interact directly with the DOM, this implies a performance loss. Using the React state allows you to avoid such thing!

Categories

Resources