0

This animation portion works really well on slideUp. But the slideDown animation (close) doesn't get triggered. There is an x on the modal that closes it. Is it because of the hook? This is the second time i'm using hook and first time to create a custom hook so was pretty stoked when it actually worked! But I'm stuck on the close animation part.

I tried using transition animation, but that didn't work even for slide up, so decided to move to keyframes.

Any help is appreciated. Thanks!

Component:

import React from 'react';
import PropTypes from 'prop-types';
import ReactModal from 'react-modal';
import classNames from 'classnames';
import styles from './styles.scss';

const Modal = ({isVisible, hide, header, children}) => {
  const closeStyles = classNames('close-icon', styles.modalClose);
  const headerStyles = classNames('h3', styles.header);
  const slideAnimation = isVisible ? styles.slideUp : styles.slideDown;
  const className = classNames(styles.commonModalStyles, slideAnimation)

  return (
    <ReactModal
      isOpen={isVisible}
      className={className}
      overlayClassName={styles.overlay}
      contentLabel={header}
    >
      <div className={styles.headerContainer}>
        <h3 className={headerStyles}>{header}</h3>
        <div className={closeStyles} onClick={hide}/>
      </div>
      <div className={styles.modal}>{children}</div>
    </ReactModal>
  );
};

Modal.propTypes = {
  isVisible: PropTypes.bool,
  hide: PropTypes.func,
  header: PropsTypes.string,
  children: PropTypes.node,
};

Modal.defaultProps = {
  isVisible: false,
};

export default Modal;

Custom Hook:

import { useState } from 'react';

const useToggle = () => {
  const [isVisible, setIsVisible] = useState(false);

  function toggle(event) {
    event.preventDefault();
    setIsVisible(!isVisible);
  }

  return {
    isVisible,
    toggle,
  }
};

export default useToggle;

Usage:

// import from file and render inside functional component.

  const {isVisible, toggle} = useToggle();
 <ModalV2 isVisible={isVisible} hide={toggle} header="Custom Header"> some content </Modal>

My css code:

.overlay {
  overflow: auto;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.slideUp {
  animation: slideup .2s ease-in-out;
}

.slideDown {
  animation: slidedown .2s linear;
}

@keyframes slideup {
  0% {transform: translateY(100%);}
  100% {transform: translateY(0);}
}

@keyframes slidedown {
  0% {transform: translateY(0);}
  100% {transform: translateY(100%);}
}

.commonModalStyles {
  overflow: auto;
  position: fixed;
  width: 100%;
  height: 100%;
}

1 Answer 1

1

I wrote a custom hook to solve this issue at work.

import { useState, useEffect, useRef, useLayoutEffect } from "react";

type UseTransition = (toggle: boolean) => [any, boolean];

const useTransition: UseTransition = toggle => {
  const didMountRef = useRef(false);
  const componentRef: any = useRef(null);
  const [isAnimating, setIsAnimating] = useState(false);

  useLayoutEffect(() => {
    if (didMountRef.current) {
      setIsAnimating(true);
    } else {
      didMountRef.current = true;
    }
  }, [toggle]);

  useEffect(() => {
    componentRef.current.addEventListener("transitionend", () => {
      setIsAnimating(false);
    });
  }, [componentRef, setIsAnimating]);

  return [componentRef, isAnimating];
};

export default useTransition;

See it in action here:

https://codesandbox.io/s/peaceful-grothendieck-42zij

Not the answer you're looking for? Browse other questions tagged or ask your own question.