Jump to content
Search Community

React GSAP animation not running locally, but working on codepen.io

Hammaad-m test
Moderator Tag

Recommended Posts

I'm unable to use the gsap.utils.selector to animate child components in my local React environment. The same code works in the codepen linked, but no animation is played when I run it locally. There are also no errors in the console. 

 

I've attached my React code, the App component is the exact same but the animation simply doesn't work. index.js

Any help would be much appreciated. 

 

See the Pen VwQWGGB by ERROR_Invalid_Syntax (@ERROR_Invalid_Syntax) on CodePen

Link to comment
Share on other sites

@Hammaad-mi wish to know, have you installed gsap locally as a dependency in your project or you are pulling from the cdn, 

another source of error might be that your gsap instance is not properly setup because i have run your code on my local machine and it works after some minor adjustments.

 

recomendations: 

- Verify that you have gsap installed on your local system or if you are using a cdn, verify that your network is okay.

-Verify your gsap instance 

- make sure your react and react-dom versions are

"react": "^18.1.0",
"react-dom": "^18.1.0",

 

  • Like 1
Link to comment
Share on other sites

I bet the problem is that React 18 runs in "strict" mode locally by default and in that new version, it causes your useEffect() to get called TWICE! Very annoying. It has caused a lot of headaches for a lot of people. So if you use a .from() tween, can you see how that would mess up the logic? .from() tweens use the CURRENT value as the destination and it renders immediately the value you set in the tween, so when it's called the first time it'd work great but if you call it twice, the CURRENT value will be the one that was immediately rendered the first time, thus nothing will happen. 

 

For example, let's say el.x is 0 and you do this: 

useEffect(() => {
  gsap.from(el, {x: 100})
}, []);

Calling that the first time would make el.x jump immediately to 100 and start animating backwards toward the current value which is 0. But the second time, it would jump to 100 (same) and animate back to the current value which is....100! Animating from 100 to 100 looks like nothing is happening. See the issue? It's a logic thing. 

 

So you can either turn off strict mode in React or you can add some conditional logic to your useEffect() call so that it only runs ONCE. 

Link to comment
Share on other sites

  • 1 year later...

i have same issue

"use client"

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

import gsap from "gsap"

import Hero from "@components/hero"
import Loader from "@components/loader"

import { useIsomorphicLayoutEffect } from "@hooks/useIsomorphicEffect"

type Timeline = gsap.core.Timeline
let callCount = 0

export default function Home() {
  const [loaderFinished, setLoaderFinished] = useState(false)
  const [tl, setTl] = useState<Timeline>()

  useIsomorphicLayoutEffect(() => {
    const ctx = gsap.context(() => {
      const tl = gsap.timeline({
        onComplete: () => setLoaderFinished(true),
      })
      setTl(tl)
    })
    return () => ctx.revert()
  }, [])
  return <main>{loaderFinished ? <Hero /> : <Loader timeline={tl} />}</main>
}

 

Link to comment
Share on other sites

38 minutes ago, GreenSock said:

@YgorLuiz if you'd like some help, please make sure you provide a minimal demo (like a Stackblitz) that clearly illustrates the problem and we'd be happy to take a look. Here's a starter template you can fork: 

https://stackblitz.com/edit/react-cxv92j

It's ok! 

Production - https://ultra-agency-q649bdcw9-ygorluiz.vercel.app

Local -  https://stackblitz.com/edit/github-6wno9z-29sc1b?file=src%2Fapp%2Fpage.tsx

 

Here we have the preloader animation working normally in production , however we do not have the same result locally the animation does not start.

 

tks

Link to comment
Share on other sites

It looks like the old "React 18+ double-invokes useEffect()/useLayoutEffect()" thing. 

 

You're correctly using gsap.context() initially, but then you've got an onComplete firing another method that creates other animations but you didn't add that to the context properly, so those don't get reverted when React runs the cleanup function. 

 

Remember, gsap.context() only records the tweens/timelines/ScrollTriggers that are created while that function is being invoked. But since you're creating things in an onComplete which fires LATER, the function has already finished being invoked, thus those new animations aren't recorded in there. 

 

It's a simple change: 

useIsomorphicLayoutEffect(() => {
    const ctx = gsap.context((self) => {
      const tl = gsap.timeline({
        onComplete: () => self.add(() => setLoaderFinished(true)), // <-- this is the key
      })
      setTl(tl)
    })
    return () => ctx.revert()
  }, [])

Does that resolve things for you? 

Link to comment
Share on other sites

I had (and have) very little time to dig into your demo, but I'd recommend going through and making sure you are doing proper cleanup. It looks to me like you've got a useIsomorphicLayoutEffect() in index.tsx that isn't doing proper cleanup. Again, React 18+ will double-invoke those functions which means you're probably accidentally creating a bunch of duplicate things and not cleaning them up. gsap.context() makes cleanup for GSAP-related stuff quite simple. I'm not a React guy, so I'm not in a great position to walk through all your code and make sure you're doing everything in the proper "React way" but based on your description of the problem, it's almost surely because React double-invokes those functions [only] in strict mode.

 

You'll definitely increase your chances of getting a good answer if you greatly simplify your demo. 

 

Perhaps @Rodrigo will have some ideas for you (he's more experienced with React).

Link to comment
Share on other sites

Hi,

 

Is great that you were able to find a way to solve your particular issue. Strict mode runs only on development so this type of problems normally appear there. Still is a good idea to use GSAP context because it'll help killing and reverting any GSAP instance that could be running when a component is unmounted. If a GSAP instance is running after a component is unmounted, this could lead to unexpected behaviour and/or memory leaks.

 

What you can do to share GSAP context instance in different places of your component is to store it on a ref or state and use the add method. Then in a useEffect hook with an empty dependencies array you can revert the context instance in the clean up return function.

 

Hopefully this helps.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...