React - Show/hide when mouse hovers using hooks - javascript

I'm trying to create a show/hide div for when my mouse enters and leaves the container. I was following this example here (https://upmostly.com/tutorials/react-onhover-event-handling-with-examples) but I've run into a problem when I hover my mouse over the 'delete-container' div, it disappears. This problem occurs because it takes the action onMouseLeave. Is there a way I can fix this or possibly make my solution better? Here is a demo of what is happening (https://i.gyazo.com/528d909625b6b3828325c4e62894d1c3.mp4).
import React, { useState } from 'react';
import axios from 'axios';
export default function JobInfo({ jobs_info }) {
const [isShown, setIsShown] = useState(false);
const changeBackground = (x) => {
x.target.style.backgroundImage =
'linear-gradient(to right, #4639a7, #78019c)';
};
const changeBackground2 = (x) => {
x.target.style.background = 'rgb(37, 45, 73)';
};
return (
<div className='row'>
<div
className='container'
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
>
<div className='row'>
<div className='job-title'>{jobs_info.title}</div>
</div>
<div className='row wrapper'>
<div className='category-title'>Category</div>
<div className='location-title'>Location</div>
<div className='type-title'>Type of Job</div>
<div className='creator-title'>Job Creator</div>
</div>
<div className='row wrapper'>
<div className='category'>{jobs_info.job_team.title}</div>
<div className='location'>
{jobs_info.job_location.title}
</div>
<div className='type'>{jobs_info.job_work_type.title}</div>
<div className='creator'>{jobs_info.user.name}</div>
</div>
</div>
<div
className='counter-container'
id='counter-container'
onMouseEnter={changeBackground}
onMouseLeave={changeBackground2}
>
Candidates <br />
{jobs_info.candidates_count}
</div>
{isShown && (
<div className='delete-container center'>
<ion-icon id='trash' name='trash'></ion-icon>
</div>
)}
</div>
);
}

The issue happens because delete-container is outside of the container hierarchy and hence mouseleave event is triggered on the container. Render the delete-container as a child of container and style it accordingly
return (
<div className='row'>
<div
className='container'
onMouseEnter={() => setIsShown(true)}
onMouseLeave={() => setIsShown(false)}
>
<div className='row'>
<div className='job-title'>{jobs_info.title}</div>
</div>
<div className='row wrapper'>
<div className='category-title'>Category</div>
<div className='location-title'>Location</div>
<div className='type-title'>Type of Job</div>
<div className='creator-title'>Job Creator</div>
</div>
<div className='row wrapper'>
<div className='category'>{jobs_info.job_team.title}</div>
<div className='location'>
{jobs_info.job_location.title}
</div>
<div className='type'>{jobs_info.job_work_type.title}</div>
<div className='creator'>{jobs_info.user.name}</div>
</div>
{isShown && (
<div className='delete-container center'>
<ion-icon id='trash' name='trash'></ion-icon>
</div>
)}
</div>
<div
className='counter-container'
id='counter-container'
onMouseEnter={changeBackground}
onMouseLeave={changeBackground2}
>
Candidates <br />
{jobs_info.candidates_count}
</div>
</div>
);
However you could simply use CSS hover event to show or hide the delete-container without the need for state after renderingdelete-containeras a child ofcontainer`
.scss
.container {
.delete-container {
display: none;
// other styles
}
&:hover {
.delete-container {
display: block;
}
}
// other styles
}

Related

Add +1 to willWatch when <a> is clicked

Task: add +1 to willWatch when <a> is clicked.
I have an error when <a> is clicked, because MovieItem is not a component. I try to set class MovieItem... but I have a problem with moviesData
import React, { Component } from "react";
import { moviesData } from "../moviesData";
function MovieItem(props) {
let {info : { id, vote_count , video, vote_average, title, popularity, poster_path, original_language, original_title ,backdrop_path, adult, overview} } = props;
return (
<div className="col" id={id} style={{width: "18rem"}}>
<img className="card-img-top" src={'https://image.tmdb.org/t/p/w500' + poster_path}
alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Оригинальное название: {original_title}</h5>
<h5 className="card-title">Название: {title}</h5>
<p className="card-text">{overview}</p>
<p className="card-text">Рейтинг: {vote_average}</p>
<p className="card-text">Популярность: {popularity}</p>
<p className="card-text">Наличие видео: {video}</p>
<p className="card-text">Оригинальный язык: {original_language}</p>
<p className="card-text">Возраст 18+: {adult}</p>
<p className="card-text">backdrop_path {backdrop_path}</p>
<p className="card-text">Голоса: {vote_count}</p>
<a
// onClick={this.props.counter}
href="#"
className="btn btn-primary">Will Watch
</a>
</div>
</div>
)
}
class App extends Component {
constructor(state) {
super(state);
this.state = {
willWatch: 0
};
this.counter = this.counter.bind(this)
}
counter(e) {
e.preventDefault();
this.state.willWatch = this.state.willWatch + 1
}
render() {
return (
<div className="container">
<div className="col-12">
<div className="row">
<div className="col-9">
<div className="row">
{
moviesData.map((props) => {
return <MovieItem info={props} counter={this.counter}/>
})
}
</div>
</div>
<div className="col-3 sidebar">
<div className="row">
<p> Хочу посмотреть, фильмов: {this.state.willWatch} </p>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default App;
First of all, you have to destructure count from props like so:
function MovieItem(props) {
let {info : { id, vote_count , video, vote_average, title, popularity, poster_path, original_language, original_title ,backdrop_path, adult, overview}, counter } = props;
return (
<div className="col" id={id} style={{width: "18rem"}}>
<img className="card-img-top" src={'https://image.tmdb.org/t/p/w500' + poster_path}
alt="Card image cap"/>
<div className="card-body">
<h5 className="card-title">Оригинальное название: {original_title}</h5>
<h5 className="card-title">Название: {title}</h5>
<p className="card-text">{overview}</p>
<p className="card-text">Рейтинг: {vote_average}</p>
<p className="card-text">Популярность: {popularity}</p>
<p className="card-text">Наличие видео: {video}</p>
<p className="card-text">Оригинальный язык: {original_language}</p>
<p className="card-text">Возраст 18+: {adult}</p>
<p className="card-text">backdrop_path {backdrop_path}</p>
<p className="card-text">Голоса: {vote_count}</p>
<a
onClick={counter}
href="#"
className="btn btn-primary">Will Watch
</a>
</div>
</div>
)
}
and one more note, you have to set state using setState function:
counter(e) {
e.preventDefault();
this.setState((prevState) => {
return {
willWatch: state.willWatch + 1
}
});
}
You should never mutate/set this.state value directly.
Or else React would not know whether state has been changed or not.
(Refer to this article for details).
So instead of updating willWatch directly,
counter(e) {
e.preventDefault();
this.state.willWatch = this.state.willWatch + 1
}
Use setState
counter(e) {
e.preventDefault();
this.state(prevState => ({willWatch: prevState.willWatch + 1}));
}

React.js - Close and add another row div after X items

I'm learning React.js.
I have this code:
const articles = Object
.keys(this.state.articles)
.map(key => <ArticleThumb key={key} details={this.state.articles[key]} />)
;
return (
<div className="container">
<div className="row">
{ articles }
</div>
</div>
)
Assuming I have 6 items, here is the code after rendering:
<div className="container">
<div className="row">
<div id="item1"></div>
<div id="item2"></div>
<div id="item3"></div>
<div id="item4"></div>
<div id="item5"></div>
<div id="item6"></div>
</div>
</div>
But I want to have only 3 items per line like this:
<div className="container">
<div className="row">
<div id="item1"></div>
<div id="item2"></div>
<div id="item3"></div>
</div>
<div className="row">
<div id="item4"></div>
<div id="item5"></div>
<div id="item6"></div>
</div>
</div>
What is the best way to do this?
The easy answer is to use something like lodash's chunk function https://lodash.com/docs/4.17.4#chunk
However, if you are not using npm and or do not want to add additional dependencies you are going to have to use a good old fashioned javascript for loop to perform the grouping you need.
With the markup you have you could do it via css:
.container {
display: flex;
flex-wrap: wrap;
}
.row {
width: 33%;
box-sizing: border-box;
}
I made a ArticleGrid component:
class ArticleGrid extends Component {
render() {
let articlesRows = Object
.keys(this.props.rows)
.map(key => <ArticleRow key={key} articles={this.props.rows[key]} />)
;
return (
<div id="articles" className="container">
{articlesRows}
</div>
);
}
}
export default ArticleGrid;
A ArticleRow component:
class ArticleRow extends Component {
render() {
let articles = Object
.keys(this.props.articles)
.map(key => <ArticleThumb key={key} details={this.props.articles[key]} />)
;
return (
<div className="row">
{articles}
</div>
);
}
}
export default ArticleRow;
I used lodash's chunk and values function (ty Deadron) and it's done:
let articles = _.values(this.state.articles);
let articlesRows = _.chunk(articles, 3);
return (<ArticleGrid rows={articlesRows} />);

React.js+Bootstrap rendering rows

I am using bootstrap and I want to render 4 columns in row. However I do not know how many columns I will have, so it should work for any number of columns and also first column can be different react component then other columns.
What I want to achieve:
<div class="row">
<div class="col-md-3 component-type-1">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
</div>
<div class="row">
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
<div class="col-md-3 component-type-2">...</div>
</div>
I tried something but did not managed to achieve what I want..
import chunk from 'lodash/chunk.js'
class Test extends React.Component {
render() {
let component1=null;
if (this.state.shouldBeComponent1) {
component1=<Component1 key="component1" />;
}
let items = [];
if (component1!=null) items.push(component1);
this.state.dataForComponents2.forEach((data) => {
items.push(<Component2 key={data.ID} />)
});
const rows = chunk(items, 4);
let pageBody=null;
if (items.length>0) {
pageBody=(
rows.map((row) => {
<div className="row">
{
row.map((item) => (
{item}
))
}
</div>})
);
}
return (
<div>
<div className="header">
///Other unreleated code
</div>
{pageBody}
</div>
);
}
Seems something wrong with your pageBody
Try this.
let pageBody= [];
if (items.length>0) {
rows.forEach((row, idx) => {
pageBody.push
(
<div key={idx} className="row">
{row}
</div>
)}
);
}
And example here

React onClick preinitialization

This is my React.component code. OnClick event executed on page initialization and i cant fix this, i tried 2 pull onClick over the maping, but this has no sense:
handleClick (id){
alert(id);
}
render() {
var tmp = this.handleClick;
return(
<div className="thirteen wide column">
<div className="rrc-title">Магазины, нарушающие РРЦ</div>
<div className="custom-hr"></div>
<div className="ui grid">
{
jsonResult.data.map(function(el){
return (
<div className="three wide column">
<div className="store-item-bad" onClick={tmp(el.siteId)}>
<div className="siteName">{el.siteName}</div>
{
el.values.map(function(element){
var processWidth ={
background : element.color,
width: element.percent+'%'
}
return(
<div className="BarItem">
<div className="progressBar">
<div className="ui tiny progress">
<div className="bar" style={processWidth}></div>
</div>
</div>
<div className="progressCounter">{element.count}</div>
</div>)
})
}
</div>
</div>
);
})
}
</div>
<div className="rrc-title">Магазины, соблюдающие РРЦ</div>
<div className="custom-hr"></div>
<div className="ui grid">
{
jsonResult.data.map(function(el){
return (
<div className="three wide column">
<div className="store-item-ok">
<div className="siteName">{el.siteName}</div>
{
el.values.map(function(element){
var processWidth ={
background : element.color,
width: element.percent+'%'
}
return(
<div className="BarItem">
<div className="progressBar BarFloatingLeft">
<div className="ui tiny progress">
<div className="bar" style={processWidth}></div>
</div>
</div>
<div className="progressCounter">{element.count}</div>
</div>)
})
}
</div>
</div>
);
})
}
</div></div>
);
}
It's easy thing, but i can't focus right now, so i need ur help guys:)
Try:
onClick={this.handleClick.bind(this,el.siteId)} or
onClick={tmp.bind(this,el.siteId)}
And add arrow func in map:
map(e=>{})

How to show loader in reactjs and meteor?

I have a page where 3 recipes are listed along with more button. When more button is clicked more 3 recipes are listed. What i am trying to do is before listing more 3 recipes i want to show a spinner/loader icon but i could only change the text of button. How can i show loader icon as soon as more button is clicked and before those additional 3 recipes are listed. I am using meteorjs and reactjs.
my code for this is
export default class Home extends Component {
constructor(){
super();
this.state = { limit:3 , loading:false }
this.addMore = this.addMore.bind(this);
}
getMeteorData(){
let data = {};
data.recipes = [];
data.recipes = Recipes.find({},{sort:{createdAt:-1}}).fetch();
let recipeHandle = Meteor.subscribe('recipelist',this.state.limit);
if(recipeHandle.ready()){
data.recipes = Recipes.find({},{sort:{createdAt:-1},limit:this.state.limit}).fetch();
}
return data;
}
addMore(){
this.setState({
limit:this.state.limit + 3, loading: true }, () => {
this.setTimeout(()=>{
this.setState({loading:false});
},2000);
});
}
render() {
console.log('this.data.recipes',this.data.recipes);
let recipes = _.map(this.data.recipes,(recipe) => {
return <RecipeList key={recipe._id} recipe={recipe} loading={this.state.loading} />
});
return (
<div className="row">
<div className="intro blink z-depth-1">
<div className="row">
<div className="col l7">
<h1 className="heading flow-text blink">Sell your Recipe</h1>
</div>
</div>
</div>
<div className="row">
<div className="col s12">
{recipes}
</div>
</div>
<button onClick={this.addMore} type="button" className="btn coral more">More</button>
</div>
);
}
}
ReactMixin(Home.prototype, ReactMeteorData);
You could remove the loading state, and just compute the loading state from the data: {this.state.limit !== this.data.recipes.length && <img src="path_to_loader.gif" />}
I am not sure where you want to show the loader, but for example you could do:
render() {
console.log('this.data.recipes',this.data.recipes);
let recipes = _.map(this.data.recipes,(recipe) => {
return <RecipeList key={recipe._id} recipe={recipe} loading={this.state.loading} />
});
return (
<div className="row">
<div className="intro blink z-depth-1">
<div className="row">
<div className="col l7">
<h1 className="heading flow-text blink">Sell your Recipe</h1>
</div>
</div>
</div>
<div className="row">
<div className="col s12">
{recipes}
</div>
</div>
{this.state.limit !== this.data.recipes.length && <img src="path_to_loader.gif" />}
<button onClick={this.addMore} type="button" className="btn coral more">More</button>
</div>
);
}

Categories