Here is the whole code of the button. I have an issue with ripple effect. When a user clicks on the button, the component creates a new rippleElements
entity to render. Everything is fine at this point.
After animation is ended onAnimationEnd
is called. The problem is, I don't have the last added element in rippleElements
. Why? The element has been rendered and animation is ended but state didn't update? As you can see I have 2 console.log
. The first one is in useEffect
and it shows the right values. But the second one that's in the onAnimationEnd
has wrong value and that console.log
fires AFTER the first one. What's going on? :)
import { omit } from 'underscore';
import React, { FunctionComponent, ButtonHTMLAttributes, useState, useEffect } from 'react';
import uuid from 'uuid/v4';
import classnames from 'classnames';
export interface Props extends ButtonHTMLAttributes<HTMLButtonElement> {
primary: boolean,
ripple: boolean,
additionalClass?: string,
}
const Button: FunctionComponent<Props> = (props) => {
const [rippleElements, setRippleElements] = useState<JSX.Element[]>([]);
useEffect(() => {
// 1, actual - 1
console.log(rippleElements, rippleElements.length)
}, [rippleElements]);
const {primary, ripple, additionalClass, children} = props;
const classNames = classnames(
'btn',
{
primary
},
{
'with-ripple': ripple
},
additionalClass
);
function onAnimationEnd(key: string) {
console.log(rippleElements.length) // 0, actual - 1
// setRippleElements(rippleElements.filter(element => element.key !== key));
}
function newRippleElement(d: number, left: string, top: string) {
const key = uuid();
return (
<div
key={key}
className="ripple"
style={{width: d, height: d, left, top}}
onAnimationEnd={() => onAnimationEnd(key)}
>
</div>
);
}
function renderRippleElements() {
return rippleElements;
}
return (
<button
className={classNames}
{...omit(props, ['additionalClass', 'primary'])}
onClick={(event) => {
if (props.onClick) {
props.onClick(event);
}
if (ripple) {
var rect = event.currentTarget.getBoundingClientRect();
const d = Math.max(event.currentTarget.clientWidth, event.currentTarget.clientHeight);
const left = event.clientX - rect.left -d/2 + 'px';
const top = event.clientY - rect.top - d/2 + 'px';
const rippleElement = newRippleElement(d, left, top);
setRippleElements([...rippleElements, rippleElement]);
}
}}
>
{children}
{renderRippleElements()}
</button>
);
}
export default Button;