Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
toomanyforloops

GSAP animation is breaking on refresh (ReactJs)

Go to solution Solved by Rodrigo,

Recommended Posts

Hello,

 

I am going to do my best to describe what is happening because for some reason it isn't showing on Codepen(I imported react and the like.) I am using useEffect to render out the animations which is just revealing two images, the <h1/>, and a paragraph. It works fine the first time but when I refresh the page, the images get stuck all the way down the page (I set the tl.from y to 1280) and the <h1/> and <p/> are invisible. One image has a little bit of movement but not a lot.

 

For some reason it isn't executing the TweenMax.to section?

I have these dependencies in my json.package:

"gsap": "^3.6.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",

 

See the Pen xxgYrqW by Hamlington (@Hamlington) on CodePen

Link to comment
Share on other sites

  • Solution

Hi,

 

Your problem could be here:

let tl = new TimelineLite({ delay: 0.8 });

useEffect(() => {}, [tl]);

You're creating your timeline as a variable in the root scope of the component's function, that will not be kept through re-renders. Assuming that this is a header and you might be using router, every route update there could be a re-render. Also you're passing tl as a dependency in your hook's array, but tl is not a state value or a prop , so that code will run every time the state or a prop is updated.

 

Use an empty array in the useEffect() hook and store a reference to the timeline using useRef():

const tl = useRef(gsap.timeline({ paused: true }));

useEffect(() => {
  tl.current
  	// All your gsap instances here
  	.from()
}, []);

Try that and let us know how it goes.

 

Happy Tweening!!!

  • Like 1
Link to comment
Share on other sites

Thanks so much!

I named it header at first and but then changed it to the body and hadn't changed the name yet but it worked! I'll remember that tl is not a state value or prop!

Link to comment
Share on other sites

  • 3 months later...

@Rodrigo is there any reason why you define your gsap timeline outside of useEffect as opposed to inside of it?  I've got in the habit of setting my refs to timelines within useEffect and I'm wondering if there is a difference.  I was looking at this post because I noticed that when I refresh in a React project anything that is being .set() within a useEffect is reset.  If I have a container that has it's transform-origin being added via .set() for instance (just one example) that is lost when I refresh.  I am performing my .set()'s in a useEffect and attach them to gsap.timeline(); perhaps this is why?

Link to comment
Share on other sites

Don't define it outside the useEffect. React will just create a brand new one on every render and throw it away.

 

Can you create a minimal demo of the issue?

 

And if by refresh, you mean hot reloading, well that's just a dev issue. Do a hard refresh.

Link to comment
Share on other sites

The GSAP instance is being stored in a reference that will be kept through re-renders, unless you update the ref.current value by hand:

On 4/11/2021 at 7:04 PM, Rodrigo said:

store a reference to the timeline using useRef()

Perhaps that should be re-phrase to: "store the timeline in a reference using useRef()", but I believe the point is pretty clear.

 

Finally Blake is right, in most cases when using HMR, an update to the code might keep state but reset other stuff, which can result in the DOM elements going to their initial state, which can conflict specially with .from() instances and other GSAP instances. In those cases the good ol' F5 key is the only solution for that.

 

Happy Tweening!!!

  • Like 1
Link to comment
Share on other sites

7 hours ago, Rodrigo said:

The GSAP instance is being stored in a reference that will be kept through re-renders, unless you update the ref.current value by hand:

 

It will still create a new timeline on every render, it just won't change the ref. You can see this is in the logs when you click the render button.

 

See the Pen 4017a05b1145ac12988c452aae38ea37 by GreenSock (@GreenSock) on CodePen

 

  • Like 2
Link to comment
Share on other sites

There are basically 3 different approaches to saving a reference to an animation and they all have different pros and cons.

 

useRef

Pros:

Setting or changing it doesn't cause a render

Cons:

Have to remember to use current when referencing it

 

useState

Pros:

Don't have use current when referencing it

Cons: 

Setting it causes a render

 

useMemo

Pros: 

Don't have to use current when referencing it, and it can be declared directly inside the useMemo instead of an effect. It won't create and throw away a new timeline on every render like useRef and useState will.

 

// creates and throws aways new timelines after every render
const tl1 = useRef(gsap.timeline());
const [tl2] = useState(gsap.timeline());

// does not create and throw away a timeline after every render
const tl3 = useMemo(() => gsap.timeline(), []);

 

Cons: 

Doesn't work well if you need your timeline to have a scroll trigger because you typically have to pass in element references inside an effect to it.

 

  • 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.
×