Vinay Sharma
Vinay Sharma

Reputation: 3777

React JS: How to animate conditionally rendered components?

Example is a functional component in which I am rendering a div conditionally. I want this div to fade-in when rendered conditionally and fade-out vice versa.

For that, I have maintained two local state variables: render and fadeIn which are computed based on show prop passed down to the Example component.

What I've done is:

Code:

interface Props {
  show: boolean;
}

const Example: React.FC<Props> = ({ show, }) => {
  const [render, setRender] = useState(false);
  const [fadeIn, setFadeIn] = useState(false);

  useEffect(() => {
    if (show) {
      // render component conditionally
      setRender(show);

      // change state to for conditional CSS classname which will
      // animate opacity, I had to give a timeout of 10ms else the
      // component shows up abruptly
      setTimeout(() => {
        setFadeIn(show);
      }, 10);
    } else {
      // change state to change component classname for opacity animation
      setFadeIn(false);

      // hide component conditionally after 200 ms
      // because that's the transition time in CSS
      setTimeout(() => {
        setRender(false);
      }, 200);
    }
  }, [
    show,
  ]);

  return (
    <div>
      {render && (
        <div className={`container ${fadeIn ? 'show' : 'hide'}`} />
      )}
    </div>
  );
};

Stylesheet:

.container {
  width: 100px;
  height: 100px;
  background-color: black;
  transition: opacity 0.2s ease;
}

.show {
  opacity: 1;
}

.hide {
  opacity: 0;
}

I believe this is not a good coding practice to achieve the functionality and should maintain only one local state in my component. I need your suggestions on how I can solve this in a better way without using any 3rd Party Library. Thanks :)

Upvotes: 23

Views: 25731

Answers (2)

Safwat Fathi
Safwat Fathi

Reputation: 1005

Actually there is a HTML attribute for this situationes called onAnimationEnd or onAnimationStart based on your case.

Example:

export default function App() {
  const [isMounted, setIsMounted] = useState(false);
  const [showDiv, setShowDiv] = useState(false);
  return (
    <div className="App">
      <button onClick={() => {
        setIsMounted(!isMounted)
        if (!showDiv) setShowDiv(true); //We should Render our Div
      }
      }>Show/Hide</button>

      { //Conditional Rendering
        showDiv && <div
        className="transitionDiv"
        style={isMounted ? mountedStyle : unmountedStyle}
        onAnimationEnd={() => { if (!isMounted) setShowDiv(false) }}
      ></div>}
    </div>
  );
}

More info can be found in this article

Upvotes: 5

Fatemeh Qasemkhani
Fatemeh Qasemkhani

Reputation: 2042

const [render, setRender] = useState(false);

useEffect(() => {
   if(show) {
     setTimeout(() => {
       setRender(true);
     }, 2000);
   } else {
     setRender(false);
   }
}, [show]);

<div className={cs(s.render, render ? 'show' : undefined)}>
  <p>{content}</p>
</div>

Css:

.render {
  ...,
  visibility: hidden;
  opacity: 0;
  transition: all 0.6s ease;
}

.show {
  visibility: visible;
  opacity: 1;
}

Hope be helpful.

Upvotes: 4

Related Questions