Jump to content
Search Community

Animation when document's ready / React JS (hooks)

LucieB test
Moderator Tag

Recommended Posts

Hey !

I'm experimenting with React JS, and I'm wondering what is the best method to start an animation when the DOM's ready with React JS.

Plus, I don't know what method is the best to target elements in React JS. I need some advices : Should I use useCallback hook, or simply the States and Refs ?

 

Here's an example of what I did. This is very simple and minimalist. 

https://codesandbox.io/s/prod-river-xqvuu

Actually, the animation works, but as I said I would like to know if my method looks good or not.

If someone could take a look... Thanks !!

 

Take care :D 

Lucie

Link to comment
Share on other sites

Hey Lucie. You should check out our article on GSAP + React. It uses GSAP 2, but the concepts are the same:

 

5 hours ago, LucieB said:

I would like to know if my method looks good or not

Using useEffect is a fine method. It just might be a little more difficult than using classes in some cases. 

 

I'm not sure if adding the elements to the state is sufficient in all cases. It may be better to use refs for each element. I'm sure someone with more skill in React like Blake or Rodrigo will come by and comment :) 

 

Some notes about your use of GSAP usage:

  • You don't need to say new before gsap.timeline()
  • You don't need to use a timeline to do the same effects on different elements - you could use one tween with staggers instead if you'd like! Something like 
    gsap.to(state, { autoAlpha: 1, duration: 1, stagger: 1 });

    For more about staggers, check out the stagger docs page.

  • Like 3
Link to comment
Share on other sites

In fact using an useEffect or the corresponding componentDidMount in class components is the best way to be sure that the DOM elements do exist. Keep in mind though, that using that call inside an useEffect hook, will trigger that code every time the state or props of that component are updated, that's why is better to use an empty array as a second argument in the useEffect hook:

 

useEffect(()=>{
  // code in here
}, []); // empty array ensures that the code is ran only when the component is mounted

Also you don't need the useCallback hook to set the DOM elements. In fact I wouldn't recommend it, plus you're not using the hook correctly. The idea of that hook is to create and update values only when the dependencies in the array are updated. Right now you're passing an empty array. Also the constant you're creating is not being used anywhere in your code:

// This constant is never used
const el = useCallback(node => {
  if (!node) return;

  const myTitle = node.children[0];
  const mySubtitle = node.children[1];
  const myParagraph = node.children[2];

  setState(state => [myTitle, mySubtitle, myParagraph]);
}, []);

Is better to create a unique variable for the parent node element. This code achieves the same result you're getting now:

import React, { useEffect} from "react";
import { gsap } from "gsap";
import "./styles.css";

export default function App() {

  let parentNode = null;

  useEffect(() => {
    console.log(parentNode);
    gsap.set(parentNode.children, { autoAlpha: 0 });

    const tl = new gsap.timeline();
    tl.to(parentNode.children[0], { autoAlpha: 1, duration: 1 });
    tl.to(parentNode.children[1], { autoAlpha: 1, duration: 1 });
    tl.to(parentNode.children[2], { autoAlpha: 1, duration: 1 });
  }, []);

  return (
    <div className="App" ref={el => (parentNode = el)}>
      <h1>My Title</h1>
      <h2>My Subtitle</h2>
      <p>My paragraph</p>
    </div>
  );
}

Happy Tweening!!!

  • Like 6
Link to comment
Share on other sites

Thanks Rodrigo and Zach for the answers!

 

If I understand everything you said, I need to declare all my variables inside the useEffect method.

But for example, if I want to add an event on click and add an animation for the same elements (autoAlpha in the title), should I need to re-write the variable (inside the event function) ? I created an other codesandbox to illustrate my question (because my english is a little bit limited, I'm sorry).

Thanks again for your time.

 

Take care, Lucie :D 

 

Link to comment
Share on other sites

You're welcome.

 

First there is no need nor obligation to declare variables/constants inside of useEffect, especially if you plan on using those outside the scope of the useEffect hook. If you'll use just inside the hook, sure there is no problem.

 

In the case of this code:

const disappear = () => {
  const title = parentNode.children[0]; // Should I re-write this variable ?
  gsap.to(title, { autoAlpha: 0, duration: 1 });
};

There is no need to re-declare that variable since it resides inside the disappear method, so every time that method is called that variable will be created, the GSAP instance will be created and one the animation is completed both elements will be elegible for garbage collection.

 

If you want to animate something more than once during the lifetime of your app, then is better to create the variables/constants that you will use in the scope of the component. This sample uses that approach without any props or state update:

 

https://codesandbox.io/s/simple-gsap-instance-toggle-xc741

 

If the props or state of your component will be updated at some point during it's life cycle, then you have to store the GSAP instance in the component's state:

 

https://codesandbox.io/s/gsap-toggle-instance-with-hooks-t9uqr

 

In these two threads you can find some explanations why the latter approach is needed:

Happy Tweening!!!

  • Like 4
Link to comment
Share on other sites

Hey, I met some issues with this technique. Perhaps because my Wifi connection is not excellent... When I push my website to the prod and watch it, sometimes the timeline is animating before the document is ready. I mean the animation began before all the content is loaded, specially on mobile. Even if I use a delay for the tl. So my question is : should I create a state for the timeline to resolve this problem ? Thanks a lot again, and take care!

Link to comment
Share on other sites

Do you have some url we can take a look at? With the information you're providing is hard to pinpoint what the issue could be and we'll be just guessing.

 

Honestly this is quite odd. I'll assume that you're working with create react app to bundle your code, the thing about that is that it generates a single JS file with all the code for the app which should work as it did in your development environment. I assume that you're using this approach for your animation:

 

https://codesandbox.io/s/simple-gsap-instance-toggle-xc741

 

One option could be that the timeline instance is not paused when you create it.

 

Another possibility is that the app is too complex and all the rendering and painting work the browser has to do is taking too long. Have you checked the performance tab in chrome dev tools in order to see how the initial render of the site is working? This could also be a performance problem which is consistent with the fact that in mobile devices it gets worst.

 

Also you can install React Devtools and use the profiler to see how the React part of your code is working.

 

React Dev Tools for chrome:

https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=es

 

React Devtools profiler docs:

https://reactjs.org/docs/optimizing-performance.html#profiling-components-with-the-devtools-profiler

 

Short video tutorial on using and understanding the profiler:

 

  • Like 4
Link to comment
Share on other sites

Thanks again Rodrigo! Everything's working as I want! Thanks to you!

I only have a last question about targeting elements, in React for GSAP animations:

Actually I only target elements that I want to animate with useRef. No problem at all. But I'm wondering if we could create some variables to store the ref path to animate the element we want.

 

I create a new CodesandBox to illustrate my question :

https://codesandbox.io/s/target-elements-in-react-gsap3-3vz98

 

I created an animation in useEffect, when the DOM's ready. But I also created an event listener on the button onClick. And to animate the title, or the subtitle, I need to rewrite the entire path from the ref ( eg. containerRef.current.children[0] ). So, my question is : Should I store the path in a state ?

Like this :

const [title, setTitle] = useState("");

useEffect(() => {
setTitle(containerRef.current.children[0]);
}, []);

const myEvent = () => { gsap.to(title, { color: "red", duration: 1 }) }

 

I'm sorry about all my questions. I just would like to write the cleanest code as I can.

 

Thanks again, Rodrigo!

Link to comment
Share on other sites

Hey, unfortunately I'm getting a 404 error with the link for the codesandbox sample so I can't take a look at it, please check it out and see if it's still live.

 

If you want to create a constant or variable for each element you want to animate, that's fine, no problem with it. Keep in mind though that right now you have a reference for the parent element so you can access each child using it's index. If you want to avoid that, because the index might not be fully predictable, you can use a ref for each element you want to animate and store that ref in a constant. Then you can use those to create the GSAP instances.

 

If you check this sample you'll see that I'm creating a variable for the DOM element which is later replaced by the ref callback inside the JSX and a event handler to control the GSAP instance that is also created:

https://codesandbox.io/s/simple-gsap-instance-toggle-xc741?file=/src/App.js:110-117

 

There is no actual need to create a ref in the component's state a simple variable should be enough even if the component's state is updated, like this sample:

https://codesandbox.io/s/gsap-toggle-instance-with-hooks-t9uqr?file=/src/App.js:221-228

 

At the end this comes down to how comfortable you are with the code that you're writing, that the code makes sense to you and for those who could look at it in the future, so it's easy to maintain and debug.

 

Happy Tweening!!!

  • Like 2
Link to comment
Share on other sites

Hey Rodrigo, I'm sorry about the 404. I thought my link was good.

I recreated what i want to illustrate here.

 

This is not about a reverse animation. I just created two different animations, and as you can see I re-write the target for animation the element (eg. container.current.children[0]). Do you think the best method is to create a state and store inside the element I want to animate differently (to not repeat myself in writing the ref children) ?

I let you look at my code, if you have some time.
 
Thanks again for your time.
Take care!
 
Lucie
Link to comment
Share on other sites

Well performance wise there shouldn't be a difference unless we're talking about hundreds or thousands of elements, which is not the case.

 

As I mentioned throughout the thread, both patterns are equally valid and creating an extra variable or constant to access them shouldn't cause a problem. Perhaps the only advice would be to change it to this:

useEffect(() => {
  const children = container.current.children;
  tl.from(children[0], {
    autoAlpha: 0,
    yPercent: 20,
    duration: 0.75
  })
    .from(children[1], {
      autoAlpha: 0,
      yPercent: 15,
      duration: 0.5
    })
    .from(children[2].children, {
      autoAlpha: 0,
      yPercent: 25,
      duration: 1,
      stagger: { amount: 0.75 }
    })
    .from(children[3], {
      autoAlpha: 0,
      duration: 1
    })
    .play();
}, []);

That's definitely a bit better to look at and it's DRY 

 

Finally I'll repeat what I said in the previous post, there is no obligation to put the reference to the DOM element in the state, that's just a personal preference, nothing more. Depends only with what approach you're more comfortable with.

 

Happy Tweening!!!

  • Like 4
  • Thanks 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...