How to fade one react component out, then fade another in?

Viewed 282

I've got 2 components that are conditionally shown or hidden based on a string value stored in useState as showModal

{showModal === 'SIGNIN' && <SignIn />}
{showModal === 'JOIN' && <Join />}

I want to fade in one component, then when the state changes, fade it out and fade in the other component.

Can this be done with react transition group?

I tried

<TransitionGroup>
    <CSSTransition in={showModal === 'SIGNIN' ? true : false} classNames='fade' timeout={220} key={showModal}>
       <div> 
          <SignIn />
       </div>
     </CSSTransition>
     <CSSTransition in={showModal === 'JOIN' ? true : false} classNames='fade' timeout={220} key={showModal}>
       <div> 
         <Join />
       </div>
     </CSSTransition>
</TransitionGroup>

I don't get any error, one component is shown, changing showModal from 'SIGNIN' to 'JOIN' does nothing. Inspecting the divs with the timeout set to 22000 shows that no new classes have been added.

1 Answers

SwitchTransition from react transition group might help.

Example

const { useState, useEffect } = React;
const { SwitchTransition, CSSTransition } = ReactTransitionGroup;

const SignIn = () => <div className="block sign-in">Sign In</div>;

const Join = () => <div className="block join">Join</div>;

const App = () => {
const [showModal, setModal] = useState("SIGNIN");

  useEffect(() => {
      let handle;
      
      const loop = () => {
      
        setModal(state => state === "JOIN" ? "SIGNIN" : "JOIN");
      
        handle = setTimeout(loop, 2500);
      };
  
      handle = setTimeout(loop, 1000);
      
      return () => {
        clearTimeout(handle);
      }
  }, []);
  
  const addEndListener = (node, done) => {
              node.addEventListener("transitionend", done, false);
            }

return <div>
  <SwitchTransition mode="out-in">
    <CSSTransition
      key={showModal === "SIGNIN"}
      addEndListener={addEndListener}
      classNames="fade">
        {showModal === "SIGNIN" ? <SignIn/> : <Join/>}
  </CSSTransition>
</SwitchTransition>
</div>;
}

ReactDOM.render(
    <App />,
    document.getElementById('root')
  );
body {
  margin: 0;
  overflow: hidden;
  font-family: Georgia, serif;
}

.block {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  padding: 10px;
  color: white;
  width: 200px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 30px;
}

.sign-in { 
  background: #0984e3;
}

.join {
  background: #6c5ce7;
}

.fade-enter {
   opacity: 0;
    transform: translate(-100%, -50%);
}

.fade-exit {
   opacity: 1;
   transform: translate(-50%, -50%);
}

.fade-enter-active {
   opacity: 1;
   transform: translate(-50%, -50%);
}

.fade-exit-active {
   opacity: 0;
   transform: translate(100%, -50%);
}

.fade-enter-active,
.fade-exit-active {
   transition: opacity 500ms, transform 500ms;
}
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/react-transition-group@4.4.2
/dist/react-transition-group.min.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>

Related