Jump to content
Search Community

GSAP with React, Navigation not working as expected

TaiwoJazz test
Moderator Tag

Go to solution Solved by TaiwoJazz,

Recommended Posts

Hi guys, i would appreciate if someone could take a look at my minimal demo example. The animation is not workings as expected.

I just want the items to navigate in and out without any errors. 

Also, when the page loads, my console is warning me that the targes are not available which i presumed, i'm calling gsap earlier than expected.

Here is my minimal demo @Rodrigo

Link to comment
Share on other sites

You look to be on the right track. You'll want to make sure, in your useEffect, that TL exists (I didn't have a chance to fork your demo, so I'm shooting slightly in the dark) with  if (!tl.current) return;--this should, hopfully get rid of your console errors on load.

And also your play reverse code could be improved to something like: showMenu ? tl.current.play() : tl.current.reverse()

Link to comment
Share on other sites

HI @ryan_labar Appreciate the help but i still didn't work as expected.

 

import React, { useRef, useState } from 'react';
import gsap from 'gsap';
import { useGSAP } from '@gsap/react';

const MobileNav = () => {
  const [active, setActive] = useState(false);
  const [showMenu, setShowMenu] = useState(true);

  const toggleMenu = () => {
    showMenu && setActive((prev) => !prev);
    setShowMenu((prev) => !prev);
    toggleTimeline();
  };

  const container = useRef();
  const tl = useRef();

  const toggleTimeline = () => {
    active ? tl.current.play() : tl.current.reverse();
  };

  useGSAP(
    () => {
      if (!tl.current) return;
      tl.current = gsap
        .timeline({
          onReverseComplete() {
            setActive(false);
          },
        })
        .from('.navAnimate', { y: -420, duration: 0.3, ease: 'back' })
        .from(
          '.navAnimate a',
          { x: -200, duration: 0.3, opacity: 0, stagger: 0.08 },
          '-=0.4'
        )
        .reverse();
    },
    { scope: container }
  );

 

Link to comment
Share on other sites

You have a severe logic issue in your code.

 

You are creating the timeline, on that timeline you have an onComplete callback that switches the active state property. The problem arises here in this conditional rendering statement:

{active && (
  <div ref={animate} className="relative">
  </div>
)}

When the timeline is created active is false, so all that HTML doesn't get rendered, so the scope in your GSAP Context and your useGSAP hook is null:

useLayoutEffect(() => {
  ctx.current = gsap.context(() => {
    tl.current = gsap
      .timeline({
      onReverseComplete() {
        setActive(false);
      },
    })
      .from(".navAnimate", { y: -420, duration: 0.3, ease: "back" })
      .from(
      ".navAnimate a",
      { x: -200, duration: 0.3, opacity: 0, stagger: 0.08 },
      "-=0.4",
    )
      .reverse();
  }, animate);

  return () => ctx.current.revert();
}, [setActive, tl.current, animate]);

Refs in react are not reactive properties, so when they change they won't trigger a re-render, because of that the scope you're passing to GSAP Context (either directly or through the useGSAP hook) is null, so you have an invalid scope. Then all the targets inside that scope are not rendered in the DOM yet, so nothing gets animated.

 

You have to correct the conditional rendering and create the timeline with active as a dependency of your useGSAP hook (for that be sure to use the latest version of the hook 2.x and up) and run the callback function only when active is truthy:

useGSAP(() => {
  if (!active) return;
  // At this point active is truthy so we can run our code safely
}, {
  scope: conatiner,
  dependencies: [active]
});

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

  • Solution
10 minutes ago, Rodrigo said:

You have a severe logic issue in your code.

 

You are creating the timeline, on that timeline you have an onComplete callback that switches the active state property. The problem arises here in this conditional rendering statement:

{active && (
  <div ref={animate} className="relative">
  </div>
)}

When the timeline is created active is false, so all that HTML doesn't get rendered, so the scope in your GSAP Context and your useGSAP hook is null:

useLayoutEffect(() => {
  ctx.current = gsap.context(() => {
    tl.current = gsap
      .timeline({
      onReverseComplete() {
        setActive(false);
      },
    })
      .from(".navAnimate", { y: -420, duration: 0.3, ease: "back" })
      .from(
      ".navAnimate a",
      { x: -200, duration: 0.3, opacity: 0, stagger: 0.08 },
      "-=0.4",
    )
      .reverse();
  }, animate);

  return () => ctx.current.revert();
}, [setActive, tl.current, animate]);

Refs in react are not reactive properties, so when they change they won't trigger a re-render, because of that the scope you're passing to GSAP Context (either directly or through the useGSAP hook) is null, so you have an invalid scope. Then all the targets inside that scope are not rendered in the DOM yet, so nothing gets animated.

 

You have to correct the conditional rendering and create the timeline with active as a dependency of your useGSAP hook (for that be sure to use the latest version of the hook 2.x and up) and run the callback function only when active is truthy:

useGSAP(() => {
  if (!active) return;
  // At this point active is truthy so we can run our code safely
}, {
  scope: conatiner,
  dependencies: [active]
});

 

Hopefully this helps.

Happy Tweening!

@Rodrigo Did you see my minimal demo? I think I submitted the wrong link. I edited the post, I don't know why it didn't change!

 

I'm using the latest useGsap here.

 

https://stackblitz.com/edit/gsap-react-basic-f48716-fmdgny?file=src%2FApp.js

 

Link to comment
Share on other sites

Hi,

 

What exactly is the issue here? Maybe I'm missing something but everything is working as expected.

 

Maybe you need to include React Router in your project, at that point my advice would be to use a layout outside the main router file in order to prevent re-rendering of the menu so the timeline can play and reverse regardless of the route changes.

 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

On 12/8/2023 at 8:16 PM, Rodrigo said:

You have a severe logic issue in your code.

 

You are creating the timeline, on that timeline you have an onComplete callback that switches the active state property. The problem arises here in this conditional rendering statement:

{active && (
  <div ref={animate} className="relative">
  </div>
)}

.............

 

You have to correct the conditional rendering and create the timeline with active as a dependency of your useGSAP hook (for that be sure to use the latest version of the hook 2.x and up) and run the callback function only when active is truthy:

useGSAP(() => {
  if (!active) return;
  // At this point active is truthy so we can run our code safely
}, {
  scope: conatiner,
  dependencies: [active]
});

 

Hopefully this helps.

Happy Tweening!

Can I have multiple useGSAP hooks?

I'm asking as I have a similar situation to this user, where I want to animate things, but only if a state is active.

However, I have it 1 step further, where I have a situation like this with multiple states.
active1
active2
active3

active4

active5
Only 1 of them would be active at a time though

 

So then, can I create seperate useGSAP hooks for each of those, or how would I then handle something like that?

Link to comment
Share on other sites

Hi @newguy123,

3 hours ago, newguy123 said:

Can I have multiple useGSAP hooks?

Definitely, useGSAP is just a convenience to avoid some of the hassle that comes with useEffect/useLayoutEffect hooks. Normally users don't need a dependencies array so they tend to forget, solved!  useGSAP uses an empty array by default. More than a few times we see users not creating a GSAP Context instance and/or forgetting about proper cleanup, solved! useGSAP creates (and returns) a GSAP Context and does the cleanup internally. In some occasions users create a GSAP Context, do proper cleanup but then in a click event they would create GSAP Tweens/Timelines that are later not properly reverted in the cleanup phase of a React component, leading to errors and memory leaks, solved! useGSAP returns contextSafe a method that allows you to add GSAP instances to the GSAP Context created internally, that will be reverted in the cleanup phase. As you can see useGSAP is a tool with quite some helpful features that aims to ease the integration of GSAP in React projects, with a simple and clear API.

 

In your case you can definitely pass a dependencies array to your useGSAP instance and create Tweens/Timelines/Draggable/ScrollTrigger instances that will be reverted when the dependency in the array is updated, so if you have five different values for an active state property you can definitely do this:

const container = useRef();
const [active, setActive] = useState();

useGSAP(() => {
  // GSAP Code Here!
}, {
  scope: container,
  dependencies: [active]
});

Hopefully this helps. If you keep having issues please create a minimal demo that clearly illustrates the problem you're having.

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

41 minutes ago, Rodrigo said:

Hi @newguy123,

Definitely, useGSAP is just a convenience to avoid some of the hassle that comes with useEffect/useLayoutEffect hooks. Normally users don't need a dependencies array so they tend to forget, solved!  useGSAP uses an empty array by default. More than a few times we see users not creating a GSAP Context instance and/or forgetting about proper cleanup, solved! useGSAP creates (and returns) a GSAP Context and does the cleanup internally. In some occasions users create a GSAP Context, do proper cleanup but then in a click event they would create GSAP Tweens/Timelines that are later not properly reverted in the cleanup phase of a React component, leading to errors and memory leaks, solved! useGSAP returns contextSafe a method that allows you to add GSAP instances to the GSAP Context created internally, that will be reverted in the cleanup phase. As you can see useGSAP is a tool with quite some helpful features that aims to ease the integration of GSAP in React projects, with a simple and clear API.

 

In your case you can definitely pass a dependencies array to your useGSAP instance and create Tweens/Timelines/Draggable/ScrollTrigger instances that will be reverted when the dependency in the array is updated, so if you have five different values for an active state property you can definitely do this:

const container = useRef();
const [active, setActive] = useState();

useGSAP(() => {
  // GSAP Code Here!
}, {
  scope: container,
  dependencies: [active]
});

Hopefully this helps. If you keep having issues please create a minimal demo that clearly illustrates the problem you're having.

Happy Tweening!

Thanks Rodrigo, I'll open a new thread, so I dont flood this user's thread with something that may be irrelevant to them....

 

here's my new thread:

 

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