Add/Modify className Dynamically onMouseEnter - javascript

I want to add a new className when the user hovers on a slick slider Image and perform some CSS transition for that particular Image card in the slider. https://stackblitz.com/edit/react-slick-slider-issues how do I add className to the slider whenever the user hovers on the image or change the parent className based on the hover position?
I tried the document.getElementsByClassName('unique-image') but all the images have this className as they are looped inside a map function. how can I only change unique-image className if the user hovers on a certain image to unique-image-hover?

You may access Event.target that triggered mouseEnter and use Element.classList add() method to add your desired className
So, your mouseEnter handler may look like that:
const mouseHover = e =>{
e.target.classList.add('someClassNameYouWantedToAdd')
}

I can use React.useState
const [hoveredClass, setHoveredClass] = React.useState("");
const updateHovered = (e) => {
setHoveredClass(e.target.id)
}
const removeHovered = (e) => {
setHoveredClass('')
}
return (
<div className={`someStaticClass ${hoveredClass ? "hoveredClass" : ""}`}
onMouseEnter={updateHovered}
onMouseExit={removeHovered}
>
{list. map(item => (
<ImageChildComponent {...item} />
)}
</div>
)
Target will give you a child element, but you can add an event listener to the parent.
As #YevgenGorbunkov mention, change in state will trigger rerendering, so
consider wrapping ImageChildComponent with React memo to prevent unnecessary rendering

Related

React onMouseOver not capturing when mouse dragging an element

So I have this React component called Between.
export default function Between({currentlyMovingElement}) {
const [lightUp, setLightUp] = useState(false);
const backgroundColor = lightUp ? "blue" : "";
useEffect(()=>{
if (!currentlyMovingElement) setLightUp(false);
}, [currentlyMovingElement]);
return <div
onMouseOver={()=>currentlyMovingElement ? setLightUp(true) : ()=>{}}
onMouseOverCapture={()=>currentlyMovingElement ? setLightUp(true) : ()=>{}}
onMouseLeave={()=>setLightUp(false)}
style={{height: "100px", width: "100%", backgroundColor}}></div>;
}
When an element is currently being moved (dragged using onMouseDown, onMouseMove and onMouseUp - see here: Child elements re-rendered different when parent changes position), I want, if it hovers over the Between element, to have the Between element "light up" (change visually). The problem is, when I am dragging an element, the onMouseOver event isn't captured (and neither is onMouseMove). The only workaround I can think of is adding an event listener to the body and checking to see if the mouse is inside the element but I'm not too keen on that. Any idea how I could fix this?

Handling an onClick on a component in React

Suppose I have a react app, and I have component A and component CountryCard, suppose that component A displays several times component CountryCard, as a deck of cards, by using a map instruction as in
{this.props.countries.map(country => <CountryCard country={country}...
How do I handle a click on one CountryCard?, is it enough that I use onClick, as
{this.props.countries.map(country => <CountryCard country={country} onClick={this.handleCountryClick(country)}...
or do I have to add an event listener, how do I do that?
Thanks in advance
Rafael
You should just pass this.handleCountryClick to CardComponent
{this.props.countries.map(country => <CountryCard country={country} onCountryClick={this.handleCountryClick}...
then call props.onCountryClick inside of CountryCard, example:
<div onClick={() => this.props.onCountryClick(this.props.country)}></div>
Events should be used as there is a call back on events:
onClick={e => this.handleCountryClick(country)}
or
onClick={this.handleCountryClick}
onClick goes on Dom elements
const CountryCard = ({onClick}) => (<div onclick = {onClick}></div>)
/*maybe do a data attribute if you don't want `country` in perantasis*/
const handleCountryClick = (e) => {
var attr = e.currentTarget.getAttribute("country")
}
Here's an example of a functional component
const CountryCard = ({onClick, country}) => (<div country = {country} onclick = {onClick}></div>)
I'm just giving you an option to use data attribute so you don't have to call an event inline.
And reminding you that click events go on Dom Elements.
I don't use React classes in my work.

How to show single hidden element of list and hide the rest

I have a column list of elements, and each of them has hidden div, I want to show hidden element on click, but when I'm clicking another div, I want to hide current and show the one I clicked last, how can I do it correctly?
You could have two specific classes, one that hides element and one that shows element. when clicking on the element you can use jQuery or JavaScript to toggle the class that shows element for the hidden div of that specific element and hide everything for any other div.
The component you're rendering could take in an active prop and only render the second div if this prop is true.
With this, you could then keep track of which element is selected in the parent.
I threw together a working example with very simple content
import React, { useState } from 'react';
const ListItem = ({id, isActive, handleClick}) => {
return (
<div id={id} onClick={handleClick}>
Here is the element
{!!isActive && <div>I am the selected element</div>}
</div>
);
};
export const List = () => {
const [selectedItem, setSelectedItem] = useState();
const items = ['one', 'two', 'three'];
const handleClick = (event) => {
setSelectedItem(event.target.id);
}
return (
<div>
{items.map(item => (
<ListItem id={item} isActive={item===selectedItem} handleClick={handleClick}/>
))}
</div>
)
}

How to Focus on nested Element by passing reference to the Parent Component in React?

I have a parent Component which sends a list of data to a List component which in turn sends it to a Label Component to display each data as a label.
I want to be able to focus on the label element when i click on it so that the appropriate style is applied ..
Below is the gist :-
class ContainerComp extends React.Component {
constructor(props) {
super(props);
this.state = {
group: [1, 2, 3]
};
clickHandler = (name, ref) = > {
// I am able to get the DIV as a html element here but calling .focus() on it dosent change the style where as when i explictly add focus using chrome debugger for the element it works.
ref.focus() // not working
}
render() {
return ( <
ListComp group = {
group
}
onClick = {
clickHandler
} >
)
}
}
function ListComp(props) {
const data = props.group.map(... < label onClick = {} > )
return ( <
Label.. >
)
}
function Label(props) {
let ref = createref();
// on focus style for the component is defined in this component
// i am making use of css modules
return ( <
div ref = {
ref
}
onClick = (name, ref) >
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
How can we achieve such a functionality without having to pass a selected prop to the label component ? By default i would select the first element and keep the focus .. or we can make it configurable.
Usually for this I would use Redux and fire off an action which therefore sets the property of the component that needs change, and make a listener that will listen for that specific prop and change style accordingly.
In this situation, id just pass down the event handler to the child component (remember to not call it when you pass it down, so do:
{() => {eventHandler()}}
and then in the child component do:
onClick={this.props.eventHandler(e)}
You will use the event to identify which element triggered it and then apply the class/style/prop to it.
There was some problem with the Ref , I am not quite sure why but i changed it to use the useRef() hook.
Label Component
const elementRef = useRef(null);
return (
<div className={[externalStyle, styles.container].join(' ')} onClick={() => onClickEvent(itemName, elementRef)} ref = {elementRef} tabIndex={1}> // added tabIndex and also changed to useRef
Container Component
clickHandler = (name: string, ref) => {
ref.current.focus(); // volla it worked
}
I tried using the old form of Ref and also useRef() without null previously (el) => (const = el).
It works if some one has some explanation where i went wrong i will be happy to listen as i am able to wrap my head around. may be a nights sleep helped fix it :P

How to select the clickable parent in my React component even if I click on its children or anywhere else inside the parent

I have started an application which I want to work same as weather.com next 36 hours section. The idea is when you click on each weatherCard which has a seperate component in my app you will update the below section which is my weatherDetails component based on the selected weatherCard /weather box. So I made the entire component clickable by giving it the click event via props from my stateful component which is my weatherLocation component. This is my WeatherCard component:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={props.clicked}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
And here in render method in WeatherLocation component I loop through data coming from state and give props the WeatherCard component:
const WeatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
{...report}
clicked={() => this.handleCardClick(event)}
/>
);
});
And this is the handleCardClick that I added for it just for testing:
handleCardClick = event => {
// const { reports , selectedCardInfo , activeCard } = this.state;
const selectedDate = document.getElementById(event.target.id);
console.log(event.target.id);
}
I don't want to use anchor tag as I don't need href. The click works fine by itself. But because I need to get the id of the parent which is the div with the class of weatherCard. At the moment when I click on other elements inside the card I cannot get the id because they are not the parent. The reason I need its id is when I get data with from the API I need a unique value for each card so that when you click on the card the data for that card will be shown in the other component which is the WeatherDetails component. But for now I need to be able to somehow choose that selected card and pull out the state for that unique card. Could someone help me out? Thanks.
You just need to pass the Parent component ID to your onClick function in Weather Card.
Here is your WeatherCard - Component
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
You can see that I have added props.id to your onClick function and with help of event now you can access that id from the parent component.
Now here is your Parent Component- WeatherCards
const WeatherCards = this.state.reports.map( (report, i) => {
return(
<WeatherCard
key={report.id}
id={i}
{...report}
clicked={this.handleCardClick}
/>
);
});
You can see in the code I am passing index number as id to your child component.
So this will give you an id (for now it's an index number) of the card in your onClick handler.
and Finally, here is your on click handler.
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
As of now, I am using the index as id if you want to use a unique identifier, you can change that easily.
General JavaScript solution is to differentiate the elements and .stopPropogation after you've captured the event you are targeting. A nested unordered list, <ul>would be an example. Tag the containing <li> with an .opened class upon rendering/displaying each level of nesting, tag those <li> elements accordingly, e.g. a dataset attribute such as data-make, then data-model, then data-option. You then attach and fire event listeners on the different level <li>'s.
Thank you #RutulPatel. I made your answer as the answer. But I changed your code a bit as I got your point so I wrote an answer as it is long. I think we might not need to change the WeatherCard at all and I don't pass event or any logic there. so it will be intact:
const WeatherCard = (props) => (
<div id={props.date} className="weatherCard" onClick={event => props.clicked(event, props.id)}>
<h2 className="cardDate">{props.date}</h2>
<h4>{props.forcast}</h4>
<div className="minmaxDeg">
<data>{props.min}</data>
<data>{props.max}</data>
</div>
<data>{props.rain}</data>
</div>
);
But I use your tip changing my weatherCards array to look like this:
const weatherCards = this.state.reports.map( report => {
return(
<WeatherCard
key={report.id}
id={report.date}
{...report}
clicked={() => this.handleCardClick(event, report.date)}
/>
);
});
So I use the report.date which is a unique value as my id. Also I don't pass event as a parameter to the arrow function I just pass it with the report.date to the handler:
clicked={() => this.handleCardClick(event, report.date)}
And the handler will be the same as you did:
handleCardClick = (event, weatherCardID) => {
console.log(weatherCardID)
}
I might even remove event later on from both if there was no need fo that.
Thank you again.

Categories