0

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;
Auine
  • 180
  • 1
  • 2
  • 12
  • Probably I'm facing the same problem https://stackoverflow.com/questions/53845595/wrong-react-hooks-behaviour-with-event-listener – Auine Jan 17 '20 at 17:48

1 Answers1

0

Fixed with

setRippleElements(rippleElements => rippleElements.filter(element => element.key !== key));
Auine
  • 180
  • 1
  • 2
  • 12