Jump to content
Search Community

GSAP with React

TaiwoJazz test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hi everyone, I feel like I'm just turning in a circle here for almost a week even after enrolling for Carl's Creative Coding Club course.. He's definitely doing a great job but since all gsap codes are written in vanilla js...

Also, i have gone through the GSAP + React article of gsap learns section but it's not just coming in handy...

I really want to master gsap using react... So i just wanna ask if anyone knows or has any gsap course that specifically focuses on React... Either on Udemy or anywhere.. A link will be appreciated...

Thanks in advance...

Link to comment
Share on other sites

Welcome, @TaiwoJazz

 

You said you read this article, right?:

 

Do you mind me asking what exactly you WISH was covered there (and wasn't)? Are you just looking for more demos or walk-throughs with real projects? We tried really hard to make the article rich and as concise as possible. It's difficult to find the right balance between "overwhelming...eyes glazing over from so much info" and "too concise...I wish there was more info".  

 

What exactly are you struggling with? 

Link to comment
Share on other sites

Thanks for the reply... As i said, i really took time to read the React article and followed the step by step guide... 

 

For example, targeting classes and id with gsap.context() works in some places and through"Invalid Scope, Target error is some places in my code"

 

These errors are not covered and I'm sure I'm following everything properly...

 

Sorry I can't convert this code to codepen... But I'm not sure why I'm getting target error here..

 

import React, {useRef, useLayoutEffect} from 'react';
import gsap from 'gsap';
import useLoader from '../../hooks/use-loader';
import useAnimatedLetters from '../../hooks/use-animatedletters';
import Bio from './Bio';

const About = () => {
  const aboutme = useRef();

  const {loading, loader} = useLoader();

  useLayoutEffect(() => {
    let ctx = gsap.context(() => {
      gsap
        .timeline({
          defaults: {opacity: 0, ease: 'back', duration: 6},
        })
        .from('#about', {ease: 'linear', autoAlpha: 0});
    }, aboutme);

    return () => ctx.revert();
  }, []);

  let string = 'About Me';

  const {letters: Heading} = useAnimatedLetters({
    strArray: string.split(''),
    index: 1,
  });

  return loading ? (
    loader
  ) : (
    <section ref={aboutme}
    >
      <div className="invisible px-6 lg:ml-16 flex flex-col lg:flex-row items-center justify-center gap-28 lg:40 xl:gap-72 h-full lg:h-screen mb-24 lg:mb-0">
        <div id="about">
          <h2 className="text-lightMode-100 text-3xl font-bold w-fit mb-6 mt-32 lg:mt-0">
            {Heading}
          </h2>
          <Bio />
        </div>
      </div>
    </section>
  );
};

export default About;

 

Link to comment
Share on other sites

Hi,

 

I've been using GSAP in React apps for over 5 years and have done my fare share of stuff with them. In my experience there is no such resource for a very simple reason: GSAP is framework agnostic, so is thought to work with every UI Framework that you can find: React, Vue, Svelte, Angular, Inferno, Alpine, name-it.

 

The biggest struggle I think React users have, especially those that are just starting, is think in terms of how to correctly architect and layout their React apps, especially when those apps have a lot of moving parts, such as routing, API calls, other 3rd party libraries and packages, etc. If you are just starting with React you could take a course in Udemy either one created by Stephen Grider or Brad Traversy. I took one course of Stephen Grider when I needed to learn how to use NestJS for a full stack project and I think that He does a good job in explaining and showing you how to do stuff with detail but not too slow. As far as Brad goes, I've seen a few videos in youtube and think he does a good job as well, IDK how much detail he goes into on his courses so I couldn't compare them. If you're looking for something closer to Carl's teaching style I'd say go with Stephen Grider.

 

Finally when starting, always go from small to big, never try to do too much. This is a learning process that has a curve, so as you start simple you begin with the easy stuff and then you add more complex stuff into the mix.

 

Let us know if you have more questions.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

To answer your question about why your code was throwing an error about an invalid scope, it looks like your component renders a completely different thing if it's "loading", so when your useLayoutEffect() is called, there is no aboutme element at all. It doesn't exist, but you're asking GSAP to look inside of that for '#about'. See what I mean? 

  • Like 1
Link to comment
Share on other sites

  • Solution

Indeed Jack is right.

 

The first time your app runs the reference for the element is undefined. Just pass the loaded state as a dependency in your use layout hook and it should work:

useLayoutEffect(() => {
  let ctx;
  if (loading) {
    ctx = gsap.context(() => {
    gsap
      .timeline({
        defaults: {opacity: 0, ease: 'back', duration: 6},
      })
        .from('.about', {ease: 'linear', autoAlpha: 0});
    }, aboutme);
  }

  return () => ctx && ctx.revert();
}, [loading]);

Here is a live example:

https://codesandbox.io/s/gsap-react-setup-forked-e77o04

 

It uses a set timeout instance to create the illusion of an API call, but it should illustrate the point.

 

Happy Tweening!

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

@Rodrigo Thanks a lot for that input... I've been using react for 6 months... Still a not a pro but I'm getting there and i took a very good course and always try to make my code base readable... 

 

I really appreciate your input, you are a mentor to recon with.. I will work on it now... Thanks a lot guys... Hopefully i should be in Green Club Sock soon 😂😂

 

 

 

 

  • Like 1
Link to comment
Share on other sites

@Rodrigo Sorry, Please i'm back again... I want to understand why the first code works just fine and the second code still shows target not found..

Based on the example you gave me above.. This code works just fine with no issues..

import React, {useRef, useLayoutEffect} from 'react';
import gsap from 'gsap';
import useLoader from '../../hooks/use-loader';
import useAnimatedLetters from '../../hooks/use-animatedletters';
import Bio from './Bio';

const About = () => {
  const aboutMe = useRef();

  const {loading, loader} = useLoader();

  useLayoutEffect(() => {
    let ctx;

    if (!loading) {
      ctx = gsap.context(() => {
        gsap
          .timeline({
            defaults: {opacity: 0, ease: 'back', duration: 1},
          })
          .from('#about', {ease: 'linear', autoAlpha: 0})
          .from('.content > p', {x: -100, stagger: 0.3});
      }, aboutMe);
    }

    return () => ctx && ctx.revert();
  }, [loading]);

  let string = 'About Me';

  const {letters: Heading} = useAnimatedLetters({
    strArray: string.split(''),
    index: 1,
  });

  return loading ? (
    loader
  ) : (
    <section ref={aboutMe}>
      <div
        id="about"
        className="invisible px-6 lg:ml-16 flex flex-col lg:flex-row items-center justify-center gap-28 lg:40 xl:gap-72 h-full lg:h-screen mb-24 lg:mb-0">
        <div>
          <h2 className="text-lightMode-100 text-3xl font-bold w-fit mb-6 mt-32 lg:mt-0">
            {Heading}
          </h2>
          <Bio className="content" />
        </div>
      </div>
    </section>
  );
};

export default About;




But this code doesn't work.. Only the first element (#contact) in the timeline work.. The rest element in the timeline kept showing target not found...
What may be the issue here... I think i'm doing everything right here and everything should work as expected...
 

import {useRef, useLayoutEffect} from 'react';
import gsap from 'gsap';
import useLoader from '../../hooks/use-loader';
import Heading from './Heading';
import Form from './Form/Form';

const Contact = () => {
  const contactMe = useRef();

  const {loading, loader} = useLoader();

  useLayoutEffect(() => {
    let ctx;

    if (!loading) {
      ctx = gsap.context(() => {
        gsap
          .timeline({
            defaults: {opacity: 0, ease: 'back', duration: 1},
          })
          .from('#contact', {ease: 'linear', autoAlpha: 0})
          .from('#content > p', {x: -100, stagger: 0.3})
          .from('.form div', {x: 100}, '<')
          .from('.form > input', {x: 100, stagger: 0.2}, '-=0.8')
          .from('.form textarea', {x: 100}, '-=0.8')
          .from('.form span', {x: 100}, '-=0.8');
      }, contactMe);
    }

    return () => ctx && ctx.revert();
  }, [loading]);

  return loading ? (
    loader
  ) : (
    <section ref={contactMe}>
      <div id="contact" className="invisible">
        <div className=" flex flex-col lg:flex-row items-center justify-center h-fit lg:h-screen lg:pl-16 px-8 w-screen gap-20 lg:pb-24 overflow-x-hidden">
          <Heading id="content" />
          <Form className="form" />
        </div>
      </div>
    </section>
  );
};

export default Contact;

 

Link to comment
Share on other sites

You have a couple of syntax errors.

 

FormContent.jsx

// id is a parameter, you need to destructure the props object
// Wrong
const FormContent = (id) => {}

// Right
const FormContent = ({ id }) => {}

Form.jsx

// Wrong
const Form = (ClassName) => {}

// Right
const Form = ({ className }) => {}
// In this case the prop you're passing in the parent is camelCased
<Form className="form" /> // className not ClassName

Fixing those seems to make it work the way you intend.

 

Happy Tweening!

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