Spring animation is not working in ReactJS - javascript

The below Spring animation is not working in ReactJS.
I have a Component class written like this:
import React from "react";
import { Component } from 'react'
import { Spring } from 'react-spring'
export default class Menu extends Component {
constructor(props) {
super(props);
this.state = { isMouseOver: false };
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
}
handleMouseEnter(e) {
this.setState({ isMouseOver: true });
}
handleMouseLeave() {
this.setState({ isMouseOver: false });
}
LogoText(props) {
const isMouseOver = props.isMouseOver;
const handleMouseEnter = props.handleMouseEnter;
const handleMouseLeave = props.handleMouseLeave;
if (isMouseOver) {
return (
<Spring
from={{ opacity: 0 }}
to={{ opacity: 1 }}
>
{
(props) => (
<div style={props}>
hover<!--this renders fine-->
<div className='upperDivLogoText' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<span><a href='#' style={{ color: '#d8f3dc' }} >dWare.sk</a></span>
<a href='#' style={{ color: '#95d5b2' }}>dWare.sk</a>
</div>
</div>
)
}
</Spring>
)
} else {
return (
<div className='upperDivLogoText' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<span><a href='#' style={{ color: '#d8f3dc' }} >dWare.sk</a></span>
<a href='#' style={{ color: '#95d5b2' }}>dWare.sk</a>
</div>
)
}
}
render() {
return (
<div className='upperDiv'>
<this.LogoText
isMouseOver={this.state.isMouseOver}
handleMouseEnter={this.handleMouseEnter}
handleMouseLeave={this.handleMouseLeave}
/>
<div className='lowerDiv'>
<ul className='lowerDivMenu'>
<li><a href='#'>O MNE</a></li>
<li><a href='#'>MOJE PORTFĂ“LIO</a></li>
<li><a href='#'>KONTAKT</a></li>
</ul>
</div>
</div>
)
}
}
But if I hover over upperDivLogoText it just doesn't do anything. Any suggestions on how to fix this?
SOLUTION:
It is because of Spring version 9. For anyone having such a problem just uninstall latest spring and do npm i react-spring#8.0.20

It could be that onMouseLeave is being triggered on the first div just after onMouseEnter because it is unmounted, hence causing it to appear as if nothing is happening?
Wouldn't you just want onMouseEnter on the first one and onMouseLeave only on the second rather than both on each?

As on March-2021, and as mentioned in comments under this answer, react-spring V9 isn't yet released and may have breaking changed. Hence, It would be safe to use V8.
But, in V9, the animation is working using useSpring hook and animated.div:
Example:
import { useSpring, animated } from 'react-spring' // Using hooks in V9
function LogoText({ isMouseOver, handleMouseEnter, handleMouseLeave }) {
const style = useSpring({
opacity: isMouseOver ? 1 : 0,
from: { opacity: 0 },
})
if (!isMouseOver) {
return (
<div
className="upperDivLogoText" onMouseEnter={handleMouseEnter}
>
<span>
<a href="#" style={{ color: '#d8f3dc' }}>
dWare.sk
</a>
</span>
<a href="#" style={{ color: '#95d5b2' }}>
dWare.sk
</a>
</div>
)
}
return (
<animated.div style={style}>
<div
className="upperDivLogoText" onMouseLeave={handleMouseLeave}
>
<span>
<a href="#" style={{ color: '#d8f3dc' }}>
dWare.sk
</a>
</span>
<a href="#" style={{ color: '#95d5b2' }}>
dWare.sk
</a>
</div>
</animated.div>
)
}

Related

Passing a json value to another react component

I have a parent functional component named Dashboard and a child class component named DashboardTable. I'm making a graphql call in the parent class and want to pass the result into the child like this <DashboardTable data={opportunityData}/>.
problem: I can get see the data in the parent but its not showing in the child
Here is my code. Please let me know what I'm doing wrong
Dashboard
import React, { useEffect, useState } from "react";
import "bootstrap/js/src/collapse.js";
import DashboardTable from "../DashboardTable";
import { API } from "#aws-amplify/api";
import config from "../../aws-exports";
import * as queries from "../../graphql/queries";
export default function Dashboard() {
API.configure(config);
async function asyncCall() {
const gqlreturn = await API.graphql({
query: queries.listMockOppsTables,
});
//console.log(gqlreturn.data.listMockOppsTables); // result: { "data": { "listTodos": { "items": [/* ..... */] } } }
return gqlreturn;
}
const [opportunityTable, changeOpportunityTable] = useState(asyncCall());
console.log(opportunityTable); // this works! returns a promise
return (
<div>
<section className="py-5 mt-5">
<div className="container py-5">
<h2 className="fw-bold text-center">
Your upcoming shadowing events
<br />
<br />
</h2>
<DashboardTable data={opportunityTable}></DashboardTable>
</div>
</section>
</div>
);
}
DashboardTable
import React from "react";
import "bootstrap/js/src/collapse.js";
import Navigation from "../Navigation";
import { Link } from "react-router-dom";
import { API } from "#aws-amplify/api";
import config from "../../aws-exports";
import * as queries from "../../graphql/queries";
export class DashboardTable extends React.Component {
constructor() {
super();
this.state = {
opportunityData: this.props,
};
}
render() {
console.log(this.opportunityData); // this doesnt work :( no data
return (
<div>
<div
className="row row-cols-1 row-cols-md-2 mx-auto"
style={{ maxWidth: 900 }}
>
{this.opportunityData.map((opportunity) => (
<div className="col mb-4">
<div>
<a href="#">
<img
className="rounded img-fluid shadow w-100 fit-cover"
src="assets/img/products/awsLogo.jpg"
style={{
height: 250,
}}
/>
</a>
<div className="py-4">
<span
className="badge mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.interview_type}
</span>
<span
className="badge bg mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.level}
</span>
<span
className="badge bg mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.ShadowReverse}
</span>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
export default DashboardTable;
Few pointers
Call api on mount in parent's useEffect
In child directly use the passed property in child
function Dashboard() {
API.configure(config);
async function asyncCall() {
const gqlreturn = await API.graphql({
query: queries.listMockOppsTables,
});
//console.log(gqlreturn.data.listMockOppsTables); // result: { "data": { "listTodos": { "items": [/* ..... */] } } }
return gqlreturn;
}
// initialize with empty array
const [opportunityTable, changeOpportunityTable] = useState([]);
console.log(opportunityTable); // this works! returns a promise
// call api to fetch data on mount
useEffect(( => {
const fetchData = async () => {
const response = await asyncCall();
changeOpportunityTable(response)
}
fetchData()
}, [])
return (
<div>
<section className="py-5 mt-5">
<div className="container py-5">
<h2 className="fw-bold text-center">
Your upcoming shadowing events
<br />
<br />
</h2>
<DashboardTable data={opportunityTable}></DashboardTable>
</div>
</section>
</div>
);
}
class DashboardTable extends React.Component {
constructor() {
super();
//this.state = {
// opportunityData: this.props,
//};
}
render() {
console.log(this.props.data); // this doesnt work :( no data
return (
<div>
<div
className="row row-cols-1 row-cols-md-2 mx-auto"
style={{ maxWidth: 900 }}
>
//map thru data prop {this.props.data?.map((opportunity) => (
<div className="col mb-4">
<div>
<a href="#">
<img
className="rounded img-fluid shadow w-100 fit-cover"
src="assets/img/products/awsLogo.jpg"
style={{
height: 250,
}}
/>
</a>
<div className="py-4">
<span
className="badge mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.interview_type}
</span>
<span
className="badge bg mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.level}
</span>
<span
className="badge bg mb-2"
style={{ margin: 2, backgroundColor: "#ff9900" }}
>
{opportunity.ShadowReverse}
</span>
</div>
</div>
</div>
))}
</div>
</div>
);
}
}
Hope it helps
There are some bugs in the child like this.state.opportunityData = this.props, that end part should likely be this.props.opportunityData, however to get you going with the async call in the parent component give this a try
const [opportunityTable, changeOpportunityTable] = useState([]);
async function asyncCall() {
const gqlreturn = await API.graphql({
query: queries.listMockOppsTables,
});
changeOpportunityTable(gqlreturn);
}
useEffect(() => asyncCall(), []);

How to optimize toggling className between different menu items by React?

I have header component, where I want to toggle className between all the elements of menu (if one of the elements of menu is active and user is clicking to another element - this element become active and all others no). I have a code like this
import React, { useState } from 'react';
import './header.scss';
export const Header = ({ favoriteCount }) => {
const [activeIndex, setActiveIndex] = useState(0);
function toggleClass(index) {
setActiveIndex(index);
}
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
<li>
<a
className={
activeIndex === 0
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Characters
</a>
</li>
<li>
<a
className={
activeIndex === 1
? 'header-menu__link active'
: 'header-menu__link'
}
onClick={() => {
toggleClass(0);
}}
href="##"
>
Favorites
</a>
</li>
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};
and styles to visualise toggling classes
&-menu__link {
color: lightgray;
}
.active {
color: #fff;
}
This approach is working but looks creepy. Maybe somebody knows how to optimize it?
I wouldn't use the index, I'd use the text of the item. I'd also include that text in the href so that there's an indication of what the anchor leads to. To avoid repeated code, you might put the menu items in a reusable array, something like this:
const menuItems = [
"Characters",
"Favorites",
];
export const Header = ({ favoriteCount }) => {
const [activeItem, setActiveItem] = useState("");
const setActiveItem = useCallback((event) => {
setActiveItem(event.currentTarget.href.substring(2));
}, []);
const list = menuItems.map(item =>
<li key={item}>
<a
className={`header-menu__link ${item === activeItem ? "active" : ""}`}
onClick={setActiveItem}
href={"##" + item}>
{item}
</a>
</li>
);
return (
<header className="header">
<div className="container header-container">
<ul className="header-menu">
{list}}
</ul>
<div className="header-favorite-count">
<i className="far fa-heart"></i>
{favoriteCount}
</div>
</div>
</header>
);
};

Gatsby Link component is underlined, not sure why

import React from "react"
import Logo from "../images/logo.svg"
import { Link } from "gatsby"
const ListLink = props => (
<li style={{ display: `inline-block`, marginRight: `1rem` }}>
<Link to={props.to}>{props.children}</Link>
</li>
)
export default function Layout({ children }) {
return (
<div style={{ margin: `1rem 1rem`, padding: `0 1rem` }}>
<Link to = "/" style = {{textDecoration: "none"}}>
<img src = {Logo} alt = "Logo" />
</Link>
<ul style={{ listStyle: `none`, float: `right`}}>
<ListLink to="/">Home</ListLink>
<ListLink to="/musings/">Musings</ListLink>
<ListLink to="/books/">My Books</ListLink>
</ul>
{children}
</div>
)
}
Wondering why the links and impinge are underlined. I text-decoration = none but that doesn't work. Could someone help?

onClick event never fires in React Child

I have a React-Redux application and I'm having issues launching a function on component click. When the logout button is clicked, it should trigger onClick={this.handleLogout}. However, Redux dev tools and adding a console log in the handleLogout prove that the function never executes.
Here are the two components, a standard navigation bar and a custom button component. I've left out some imports to shorten the code.
Navigation Bar
class Navigation extends React.Component {
handleLogout = () => {
this.props.logOut();
this.props.history.push("/login");
}
render() {
return (
<nav>
<span className="nav-left">
<Link className="light" to="/">
Home
</Link>
</span>
<span className="nav-right">
{this.props.authenticated ? (
<>
<Button to="/account">Account</Button>
<Button onClick={this.handleLogout} buttonStyle="danger">
Log Out
</Button>
</>
) : (
<Button to="/login">Login</Button>
)}
</span>
</nav>
);
}
}
Navigation.propTypes = {
authenticated: PropTypes.bool.isRequired,
logOut: PropTypes.func.isRequired
};
export default withRouter(Navigation);
Button
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}
Component Dev Tools
component dev tools
You forgot to pass onClick to the span that represents a button without a link
export default class Button extends Component {
state = {
classList: classNames({
button: true,
button_accent: this.props.buttonStyle === "accent",
button_danger: this.props.buttonStyle === "danger"
})
};
render() {
return (
<div className="button_container">
<div
className={classNames({
button_wrapper: true,
center: !this.props.left && !this.props.right,
left: this.props.left,
right: this.props.right
})}
>
{
this.props.to ? (
this.props.regular ? (
<a href={this.props.to} className={this.state.classList}>
{this.props.children}
</a>
) : (
<Link to={this.props.to} className={this.state.classList}>
{this.props.children}
</Link>
)
) : (
<span onClick={this.props.onClick} className={this.state.classList}>
{this.props.children}
</span>
)
}
</div>
</div>
);
}
}

Rendering component on long mouse click

I am trying to render a modal component on a long mouse click. If I just try to fire an alert it works but rendering doesn't seem to do the trick. I am assuming maybe If I have to return? Not quite sure. I created a function handleButtonPressDown to perform this task and the handleButtonRelease to clear interval in the event the user decides not to perform this action.
export class Dropdown extends React.Component<IProps> {
buttonPressTimer: any;
constructor(props: IProps) {
super(props);
this.handleButtonPress = this.handleButtonPress.bind(this);
this.handleButtonRelease = this.handleButtonRelease.bind(this);
}
public render() {
return (
<div style={{ alignSelf: "center" }}>
<ul className="nav nav-pills">
{filteredProbes.length === 0 ? (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
display: "none"
}}
></div>
</li>
) : (
<li className="nav-item dropdown ">
<div
className="dropdown-menu show"
x-placement="bottom-start"
style={{
position: "relative",
willChange: "transform",
top: "5px",
overflowY: "scroll",
maxHeight: "200px",
color: "white"
}}
>
{this.props.searchState.isActive === false
? probes.map(probe => (
<a
onClick={() => this.props.onUpdateSelectedProbe(probe)}
className="dropdown-item"
onMouseDown={this.handleButtonPress}
onMouseUp={this.handleButtonRelease}
>
<div
className="dropdown-divider"
style={{ backgroundColor: "black" }}
></div>
{probe.companyPN}: {probe.description}
</a>
))
: filteredProbes.map(filterprobe => (
<a
onClick={() =>
this.props.onUpdateSelectedProbe(filterprobe)
}
className="dropdown-item"
>
<div className="dropdown-divider"></div>
{filterprobe.companyPN}: {filterprobe.description}
</a>
))}
</div>
</li>
)}
</ul>
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
{/* Show the modal if showModal is true */}
this.props.modalState.showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
You need to move the code that you have inside setTimeout to render function and use state to render WedgeGroup:
export class Dropdown extends React.Component<IProps> {
...
constructor(props: IProps) {
super(props);
this.state = {
showModal: false
};
...
}
public render() {
const showModal = this.props.modalState.showModal &&
this.state.showModal;
return (
<div style={{ alignSelf: "center" }}>
{
showModal && (
<WedgeGroup
wedgeState={this.props.wedgeState}
onUpdateSelectedWedge={this.props.onUpdateSelectedWedge}
onUpdateShowModal={this.props.onUpdateShowModal}
onUpdateHideModal={this.props.onUpdateHideModal}
modalState={this.props.modalState}
/>
);
}
//..... render other components
</div>
);
}
handleButtonPress() {
this.buttonPressTimer = setTimeout(() => {
this.setState({
showModal: true
})
}, 1000);
}
handleButtonRelease() {
clearTimeout(this.buttonPressTimer);
}
}
It will not render firstly because your are not triggering any mechanism that makes React render.
I'd suggest to you to remove this component from the setTimeout, place it inside the render (where it should be).
And finally manipulate your component state to show or hide your modal.
If you trigger a timer to show the modal view it will only appear after the change of the state, so in your case it will take 1s to show to the user, what may look not responsive.
// inside your handleButtonPress()
this.setState({
showModal: true
}}

Categories

Resources