How to make hover event on single component? - javascript

I want to display h4 photo title on photo, but only if photo hovered. I was trying do this using onMouseEnter and onMouseLeave, but if I using many the same components, and when I hovered one of them, every components on page displaying photo title. Can I do something in my code to display title only on hovered photo?
Single Photo:
const Photo = props => (
<div onMouseEnter={props.onMouseEvent}
onMouseLeave={props.onMouseEvent}
style={props.backgroundImage}>
<h4>{props.hovered && props.title}</h4>
</div>
);
Displaying many Photo components:
class Gallery extends Component {
state = {
photos: [], // fetched photos here
hovered: false
};
toggleMouseHover = () => this.setState(prev => ({ hovered: !prev.hovered }));
render() {
return (
{this.state.photos.map(photo => {
<Photo
backgroundImage={{ backgroundImage: `url(${photo.src})` }}
title={photo.title}
onMouseEvent={this.toggleMouseHover}
hovered={this.state.hovered}
/>
})}
);
}
}
I was thinking about using content in CSS ::before or ::after, but I can not do it in React.

You can do this with pure css, it shouldn't matter that you are using react.
Single Photo:
const Photo = props => (
<div style={props.backgroundImage} className="div-photo">
<h4 className="photo-title">{props.title}</h4>
</div>
);
Css:
div.div-photo:hover > h4.photo-title {
display: block;
}
div.div-photo > h4.photo-title {
display: none;
}
I think this should work. You can use attribute visibility if you prefer it. Also, as mentioned in the comments, attribute opacity is a good option if you want to use fade-in/fade-out effects.

Related

How to change image src when onMouseEnter div in react stateless component

I'm beginner in react.
I want to change my image src when mouse enter the div.
Here is my code.
const CategoryImage = styled.img.attrs(props => ({
src: props.url,
}))`
width: 80px;
height: 80px;
margin: 5px auto;
`;
let imgUrl = ``;
const Category = ({ categoryItems }) => {
function handleHover(category) {
const {
category: { hoverUrl },
} = category;
// console.log(hoverUrl);
imgUrl = hoverUrl;
}
function handleUnHover(category) {
const {
category: { url },
} = category;
// console.log(url);
imgUrl = url;
}
return (
<Container>
<Grid>
{categoryItems.map(category => (
<CategoryContainer
key={category.id}
onMouseEnter={() => handleHover({ category })}
onMouseLeave={() => handleUnHover({ category })}
>
<CategoryImage url={imgUrl} alt={category.name} />
<CategoryName key={category.id}> {category.name} </CategoryName>
</CategoryContainer>
))}
</Grid>
</Container>
);
};
Can I change image without using state?
Most of Questions usually use state to change image. I think state isn't needed when changes occurs in my case(codes) though.
And, I heard that performance usually better without using state. Is that right?
Always Appreciate u guys:)
In case of 2 images , just add css property. Hide it by display none , and position all the images at top ....
On mouse over or enter , in this event , pass the class name , that's it .....
I did this task long back, but can't remember exactly what I had done ,
Try this

React toggle class on just one component

I'm creating, in componentDidMount, a lots of <div>'s.
constructor (props) {
super(props)
this.state = {
componentLoaded: false,
divs: []
}
}
componentDidMount () {
this.createDivs()
}
createDivs () {
// Actually, this divs are created dinamically and with infinite scroll
let divs = <div className='container'>
<div className='item' onClick={() => { /* Add class */ }}>...</div>
<div className='item' onClick={() => { /* Add class */ }}>...</div>
<div className='item' onClick={() => { /* Add class */ }}>...</div>
/* ... n divs ... */
</div>
let newDivs = this.state.divs
newDivs.push(divs)
this.setState({
componentLoaded: true,
divs: newDivs
})
}
render () {
return {this.state.componentLoaded ? this.state.divs : null }
/* In my return, if X event occurs, re-call this.createDivs() to add more divs */
}
What I'm trying to achieve, is to toggle a class into only one of the .item divs, and then if clicking another one, remove it from the before and add it to the one was clicked.
I've tried to add an attribute to the state, but it didn't add it. I also searched for some solutions, but I always find solutions which doesn't toggle, as they are "toggled individually" in separated components.
Hoping to find some help, maybe this thing is real simple, but for now, I cannot figure out how to make it.
PS: I'm adding the createDivs into the state because it's an infinite scroll that re-uses the function, so I just push them into the state and the scroll won't go to the top again when adding the previous ones + the new ones.
In problems like these it is always helpful to determine what goes into react's state. You want the state to be as lightweight as possible (so you store only the stuff which is necessary)
class Test extends React.Component {
state = {
selectedDiv: null,
};
handleClick = id => {
this.setState(prev => ({
// sets it to null if its already active else, sets it active
selectedDiv: prev.selectedDiv === id ? null : id,
}));
};
render() {
// Array to map over
const divs = [1, 2, 3, 4];
const { selectedDiv } = this.state;
return (
<div className="container">
{divs.map(div => {
return (
<div
key={div}
className={selectedDiv === div ? "item class_to_add" : "item"}
onClick={() => this.handleClick(div)}
>Item {div}</div>
);
})}
</div>
);
}
}
In the above examples we are only storing the unique Id of the div in the state and using that to determine if the selected div is active or not, if it is then we simply remove it from the state. The above solution does not require any complex lifecycle methods, my advice would be to keep the component as simple as possible.
PS. not part of the answer but I suggest you to look into the newer hooks API its more intuitive and most probably the future of react
First, note that you're breaking a React rule here:
this.state.divs.push(divs)
You must never directly modify state. The correct thing there is either:
this.setState({divs}); // Replaces any previous ones
or
this.setState(({divs: oldDivs}) => {divs: [...oldDivs, divs]}); // Adds to any previous ones
However, the "React way" to do this would probably be not to store those divs in state at all; instead, store the information related to them in state, and render them (in render) as needed, with the appropriate classes. The information about which one of them has the class would typically either be information on the items themselves, or some identifying information about the item (such as an id of some kind) held in your component's state.
Here's an example using items that have an id:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
// No items yet
items: null,
// No selected item yet
selectedId: null
};
}
componentDidMount() {
this.createDivs();
}
createDivs() {
// Simulate ajax or whatever
setTimeout(() => {
const items = [
{id: 42, label: "First item"},
{id: 12, label: "Second item"},
{id: 475, label: "third item"},
];
this.setState({items});
}, 800);
}
render () {
const {items, selectedId} = this.state;
if (!items) {
// Not loaded yet
return null;
}
return (
<div className='container'>
{items.map(({id, label}) => (
<div
key={id}
className={`item ${id === selectedId ? "selected" : ""}`}
onClick={() => this.setState({selectedId: id})}
>
{label}
</div>
))}
</div>
);
}
}
ReactDOM.render(<Example />, document.getElementById("root"));
.selected {
color: green;
}
.item {
cursor: pointer;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.0/umd/react-dom.production.min.js"></script>

How can I use conditional statements alongside inline styles correctly in Reactjs?

I am building a pizza builder react application. An application where users can build pizzas in real-time and order them. I am trying to set some conditional styles based on the state and the input Handler but my styles are not being applied.
This is my JSX code
class PizzaBuilder extends Component {
state = {
sauce: {
selected: 'Marinara'
}
}
toggleSauceHandler = (type) => () => {
this.setState({
toggle: type
});
}
render() {
return (
<div className={styles.Sauce}>
<div>
<img style={this.state.toggle==="Marinara" ? {display: 'block', position: 'absolute', zIndex: '100'}: null} src={Marinara} alt="marinara" />
</div>
</div>
<div>
<input type="radio" name="sauce" id="Marinara" value="Marinara" onChange={this.toggleSauceHandler( "Marinara")} checked={this.state.toggle==="Marinara" }/>
<label for="Marinara">Marinara</label>
</div>
)
}
}
The Sauce itself is set to display none as I do not need the images showing on the page. I only want to show the one by one based on the input from the radio button.
Check below link, may be helpful for you.
React - Change Inline CSS Conditionally Based on Component State

Clicking on image 'pushes' the rest of the list underneath image

I am trying to build a simple list component that displays images when a title is clicked. For each title you can open the corresponding image(s), that al works fine, but the problem is that the rest of the titles (the list) is pushed underneath the image(s).
The desired result should be that the images are displayed to the right of the list without affecting the list.
I've tried separating the component into two components, so where the image is called in the ternary operator putting a component like <ProjectImage src={project.src} alt={project.altText}/> but that doesn't seem to fix the problem.
import {items} from '../ProjectInfo/projectObjects'
export class Sidenav extends Component {
constructor(props) {
super(props);
this.state = {isToggleOn: false, items: items};
this.showProjectOnClick = this.showProjectOnClick.bind(this);
}
showProjectOnClick(event){
const checkActive = this.state.items.id === items.id
const activeProject = {...event, active: !checkActive}
this.setState(state => ({
isToggleOn: !state.isToggleOn,
activeProject
}));
}
render() {
const {items} = this.state
return (
<div className="sidenav">
{items.map((project) => {
return ( <div className="Box" key={project.id}>
<p className={this.state.isToggleOn && this.state.activeProject.id === project.id ? 'P_Color' : null}
onClick={() => {this.showProjectOnClick(project)}}><b>Project name: </b>{project.title}</p>
{
this.state.isToggleOn && this.state.activeProject.id === project.id
?
<div className="ProjectImageBox">
<img className="ProjectImage" src={project.src} alt={project.altText}/>
</div>
: ''
}
</div>)
})}
</div>
)
}
}
The project list
The current result when a title is clicked
The desired result
You could do it with css.
.image{
position: absolute;
top: 0;
right: 40px;
}
But a better option would be using a different component and passing the selected item to this component. This reduces the amount of components and you just have to position them accordingly. I have created a simple stackblitz showing how this works.
To do this you have to absolute position you image so it doesn't take up the space in the list.
the css class should be the following:
.image {
position: "absolute";
top: 0;
left: "100%";
/* the propreties below you can customize */
width: "50px";
height: "auto";
}

Change img on hover React re-renders all children

I need to change the url of the img on hover.
But the function trigger all of the children thats render.
I couldn't find a way to make the function trigger each children separate.
I try to make some new state to handle the indexes, but it didn't work...
const Team = React.createClass ({
getInitialState : function() {
return { hovered: false }
},
componentDidMount(){
this.props.fetchTeam();
document.title = "Equipe | [ Adesign";
},
onMouseOver : function () {
this.setState({ hovered:true });
},
onMouseOut : function () {
this.setState({ hovered:false });
},
render(){
return (
<div className="wrapper">
<div className="Team">
<HeaderAbout />
<h1>EQUIPE</h1>
<div className="content-team">
{this.props.posts.team.map((singleTeam, i) => {
var imgUrl = singleTeam.acf.photo.url;
if (this.state.hovered) {
imgUrl = singleTeam.acf.gif.url;
} else {
imgUrl = singleTeam.acf.photo.url;
}
return(
<div key={i} className="single-team col-xs-12 col-sm-6 col-md-4" onMouseOver={this.onMouseOver} onMouseOut={this.onMouseOut}>
<img className="img-responsive" src={imgUrl}/>
<p>{singleTeam.title.rendered}</p>
</div>
)
})}
</div>
</div>
</div>
)
}
});
You want to checkout the shouldComponentUpdate method:
Invoked before rendering when new props or state are being received.
This method is not called for the initial render or when forceUpdate
is used.
Use this as an opportunity to return false when you're certain that
the transition to the new props and state will not require a component
update.
In order to avoid rerendering all the images you can create a component to render an image that contains the state and the event handlers. By doing so, you prevent to rerender the parent component and its siblings whenever an image is hovered.
Edit: I just realized that your code changes all the images when any of them is hovered. Are you sure that it is what you want? In that case, you need to rerender everything. My solution is only valid if you only want to change the hovered image, leaving the others intact.

Categories