<div id="root" class="panel center"></div>
.app {
  padding: 10px;
}

.box {
  margin: 10px 0;
}
const { useEffect, useRef, useState, useCallback } = React;

function Box({ children, addAnimation, index }) {
  const el = useRef();
  
  useEffect(() => {   
    console.log("Box effect");
    const animation = gsap.to(el.current, { x: -100 });
    addAnimation(animation, index);
    
    return () => animation.progress(0).kill();
  }, [addAnimation, index]);
  
  return <div className="box" ref={el}>{children}</div>;
}

function Circle({ children, addAnimation, index, rotation }) {
  const el = useRef();
  
  useEffect(() => {  
    console.log("Circle effect");
    const animation = gsap.to(el.current, { rotate: rotation, x: 100 });
    addAnimation(animation, index);
    
    return () => animation.progress(0).kill();
  }, [addAnimation, index, rotation]);
  
  return <div className="circle" ref={el}>{children}</div>;
}


function App() {    
  const [reversed, setReversed] = useState(false);  
  const [tl, setTl] = useState(() => gsap.timeline());
  
  useEffect(() => {
    console.log("App effect");
  }, [])
  
  const addAnimation = useCallback((animation, index) => {    
    tl.add(animation, index * 0.1);
  }, [tl]);
  
  useEffect(() => {    
    console.log("Reverse effect")
    tl.reversed(reversed);
  }, [reversed, tl]);
     
  return (
    <div className="app">   
      <button onClick={() => setReversed(!reversed)}>Toggle</button>
      <Box addAnimation={addAnimation} index={0}>Box</Box>
      <Circle addAnimation={addAnimation} index={1} rotation="360">Circle</Circle>
    </div>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));
View Compiled

External CSS

  1. https://codepen.io/GreenSock/pen/gOWxmWG.css

External JavaScript

  1. https://unpkg.co/gsap@3/dist/gsap.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js