React spring use transition leave/enter collision

Viewed 174

I have two issues.

  1. Onload, the bottom links should render only once, but they are rendered twice.
  2. When clicking the box links at the bottom, the current links should smoothly fade out and the new ones fade in. Instead, the new ones initially fade in stacked with the previous ones.

enter image description here

what am I doing wrong here?

Sandbox Link

Relevant animation code:

export default function DynamicScreen({}: IProps) {
  const { screenKey } = useParams()
  const [isLeavingScreen, setIsLeavingScreen] = useState(false)
  const [screen, setScreen] = useState<IDynamicScreen>()
  const [screenLinks, setScreenLinks] = useState<IDynamicScreenLink[]>([])
  const navigate = useNavigate()
  const isFirstRenderRef = useRef(true)

  useEffect(() => {
    if (isFirstRenderRef.current) {
      isFirstRenderRef.current = false
      return
    }
  }, [])

  useEffect(() => {
    const loadScreen = async () => {
      console.log("Screen - Loading")
      const { data: _screen } = await api.getScreen(screenKey!)
      setScreen(_screen)
      setScreenLinks(
        [_screen.link1?.[0], _screen.link2?.[0], _screen.link3?.[0]].filter(
          Boolean,
        ),
      )
      console.log("Screen - Loaded")
    }

    loadScreen()
  }, [screenKey])

  const springApiLory = useSpringRef()
  const springLoryStyle = useSpring({
    from: { opacity: 0, scale: 0 },
    to: { opacity: 1, scale: 1 },
    ref: springApiLory,
  })

  const springApiTitle = useSpringRef()
  const springTitleStyle = useSpring({
    from: { opacity: 0, transform: "translateY(-25px)" },
    to: !isLeavingScreen
      ? { opacity: 1, transform: "translateY(0)" }
      : {
          opacity: 0,
          transform: "translateY(-25px)",
        },

    ref: springApiTitle,
  })

  const springApiScriptHtml = useSpringRef()
  const springScriptHtmlStyle = useSpring({
    from: { opacity: 0, transform: "translateX(-25px)" },
    to: !isLeavingScreen
      ? { opacity: 1, transform: "translateY(0)" }
      : {
          opacity: 0,
          transform: "translateX(-25px)",
        },
    ref: springApiScriptHtml,
  })

  const springApiLinks = useSpringRef()

  const linkTransition = useTransition(isLeavingScreen ? [] : screenLinks, {
    ref: springApiLinks,
    from: { opacity: 0 },
    enter: { opacity: 1 },
    leave: isLeavingScreen ? { opacity: 0 } : {},
    trail: 100,
  })

  const chain = [
    springApiLory,
    springApiTitle,
    springApiScriptHtml,
    springApiLinks,
  ]

  useChain(
    !isLeavingScreen ? chain : chain.reverse(),
    [...Array(chain.length).keys()].map(calcStaggerDelay),
  )

  const goToLink = (link: string) => {
    setIsLeavingScreen(true)
    setTimeout(() => {
      setScreenLinks([])
      navigate(`/${link}`)

      setIsLeavingScreen(false)
    }, 1000)
  }

  if (!screen) return null

  return (
    <>
      <div className="dynamic-screen-top">
        <div className="left">
          <animated.div style={springTitleStyle}>
            <h1>{screen.title}</h1>
          </animated.div>
          <animated.div style={springScriptHtmlStyle}>
            <div
              dangerouslySetInnerHTML={{
                __html: screen.scriptHtml,
              }}
            />
          </animated.div>

          {/* TODO screen.actions */}
        </div>
        <animated.div style={springLoryStyle} className="right">
          ‍♀️
        </animated.div>
      </div>
      <div className="dynamic-screen-bottom">
        {linkTransition((style, screenLink) => (
          <animated.div
            style={style}
            className="dynamic-screen-bottom-link-wrap"
          >
            <DynamicScreenLink
              linkKey={screenLink!.key}
              onClick={goToLink}
              text={screenLink!.text}
              imageUrl={screenLink!.imageUrl}
              size={screenLink!.size}
            />
          </animated.div>
        ))}
      </div>
    </>
  )
}

interface IProps {}

const calcStaggerDelay = (i: number) => i * 0.1 + 0.1

1 Answers

I don't know if this helps you continue, but what i did first of all is to set screenLinks the to an empty array when navigating to new page

const goToLink = (link: string) => {
    setScreenLinks([]);
    setIsLeavingScreen(true);
    setTimeout(() => {
      navigate(`/${link}`);
      setIsLeavingScreen(false);
    }, 300);
  };

and also, if isLeavingScreen, then set the width to zero, in order for the next screenLinks to take whole space

  const linkTransition = useTransition(isLeavingScreen ? [] : screenLinks, {
    ref: springApiLinks,
    from: { opacity: 0 },
    enter: { opacity: 1 },
    leave: isLeavingScreen ? { display: "none", width: 0 } : {},
    trail: 100
  });

demo

Related