Jump to content
GreenSock

nonoumasy

ScrollTrigger and array of refs in React

Go to solution Solved by ZachSaucier,

Recommended Posts

I used this as a guide for ScrollTrigger and React with dynamic data being mapped.

https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/

 

So it sort of works. Sort of.
My code is pretty much the same as the demo above except I'm fetching data from Firebase Firestore with a useEffect to fetch the data and then to invoke the GSAP animation. 

A Couple of things:
1) just like the demo, my headerRef does animate (and that is using the fetched data) but only that. The array of items that is mapped from the fetched data doesn't get the animation.
2)also, something wierd, if I make a change and save my React app, this will actually make the animation work, but as soon as I hit refresh on the browser, it goes back to not working. I've turned the markers on and I only see them at this time.
3)I would have added a CodePen but since this requires a Firebase and React, I thought I would just paste the relevant code below:
Hope someone can help.
 

const DetailScreen = ({ }) => {
const headerRef = useRef(null);
const { id } = useParams()
const [journal, setJournal] = useState([])
const [chapters, setChapters] = useState([])
 
const revealRefs = useRef([]);
revealRefs.current = [];
 
const addToRefs = el => {
if (el && !revealRefs.current.includes(el)) {
revealRefs.current.push(el);
}
};
 
useEffect(() => {
fetchData()
gsapAnim()
 
}, [])
 
const fetchData = async () => {
const db = firebase.firestore()
const data = await db.collection('trips').doc(id).get()
const item = data.data()
 
const data2 = await db.collection('trips').doc(id).collection('chapters').get()
const item2 = data2.docs.map(doc => (doc.data()))
setJournal(item)
setChapters(item2)
}
 
const gsapAnim = () => {
gsap.from(headerRef.current, {
autoAlpha: 0,
ease: 'none',
delay: 1
});
 
chapters && revealRefs.current.map((el, index) => {
 
gsap.fromTo(el, {
autoAlpha: 0
}, {
duration: 1,
autoAlpha: 1,
ease: 'none',
scrollTrigger: {
id: `section-${index + 1}`,
trigger: el,
start: 'top center+=100',
toggleActions: 'play none none reverse',
markers: 'true'
}
});
});
}
 
return (
<div className={"detailContainer"}>
<div ref={headerRef} className={"headerSection"}>
<img className={"journalImage"} src={journal.journalImage} alt='' />
<h2 className={"title"}>{journal.title}</h2>
<h4 className={"author"}>{journal.author}</h4>
 
<div className={"tagsSection"}>
{journal.tags && journal.tags.map(tag => (<Tags tag={tag} />))}
</div>
 
<div className={"summarySection"}>
<div style={{ fontSize: 24, fontWeight: 'bold' }}>Summary:</div>
<div className={"summary"}>
{journal.summary}
</div>
</div>
</div>
 
<div className={"entrySection"} style={{
marginTop: 120,
}}
>
{chapters.map(item => (
<div key={item.id} ref={addToRefs}>
<Entry item={item} />
</div>
))}
</div>
</div>
)
}
Link to comment
Share on other sites

  • Solution

Hey nonoumasy.

 

On 1/31/2021 at 6:32 AM, nonoumasy said:

my headerRef does animate (and that is using the fetched data) but only that. The array of items that is mapped from the fetched data doesn't get the animation.

To me that makes it sound like you're not waiting for your data to load before trying to start your animation. Looking at your code, that seems to be the case - you don't wait for your data to be fetched before starting your animation.

Link to comment
Share on other sites

That was exactly it. I added the gsap() function after setChapters() in the fetchData function and now it works some of the time.

Can you pls take a look at :

https://tripmap.netlify.app/journals/gGKrNrmT8JAYDSqBBt0X

Sometimes the section has more height sometimes less. Is this still part of the problem of not waiting for the fetched data? I added awaits on most of the fetching stuff so I'm not sure what else I can do to make sure the state is finished before calling the GSAP function.

 

FYI, on mobile its not that bad. the sections end in the right place.

 

 

thanks.

Link to comment
Share on other sites

Ok, i finally fixed this with 

useEffect(() => {
        fetchData()
        setTimeout(()=>  {
            gsapAnim()
        }, 1000);
        
    }, [])

so that 1 second gave it just enough time to fetch the data. still not sure if the array had more items if this trick would work.

Link to comment
Share on other sites

Guessing what time it's loaded is not a good way of doing things. Why not call gsapAnim as the last line of your fetchData function?

Link to comment
Share on other sites

I did add it last. The only way i could make it work was to add a little timeout.
 

  useEffect(() => {
        fetchData()
        setTimeout(()=>  {
            gsapAnim()
        }, 1000);
        
    }, [])

    const fetchData = async () => {
        const db = firebase.firestore()
        const data = await db.collection('trips').doc(id).get()
        const item = await data.data()

        const data2 = await db.collection('trips').doc(id).collection('chapters').get()
        const item2 = await data2.docs.map(doc => (doc.data()))
        await setJournal(item)
        await setChapters(item2)
        
        // await gsapAnim()
        
    }

    const gsapAnim = () =>{
        gsap.from(headerRef.current, {
            autoAlpha: 0,
            ease: 'none',
            delay: 1
        });

        revealRefs?.current?.map((el, index) => {

            gsap.fromTo(el, {
                autoAlpha: 0
            }, {
                duration: 1,
                autoAlpha: 1,
                ease: 'power2',
                scrollTrigger: {
                    id: `section-${index + 1}`,
                    trigger: el,
                    start: 'top center+=0',
                    end: 'bottom top',
                    toggleActions: 'restart none none reverse',
                    // markers: 'true',
                    scrub: 'true',
                }
            });
        });
    }

 

Link to comment
Share on other sites

Are you saying that you removed the gsapAnim call from your useEffect, uncommented it inside of the fetchData function, and it didn't work the way that you want it to?

Link to comment
Share on other sites

I tried both. I added gsapAnim to fetchData which was called in useEffect. That didnt work so I just added it after fetchData in the useEffect. and that didnt work either, well sometimes it did, but not consistent. Then when I added the useTimeout, it did work.

 

Link to comment
Share on other sites

  • 3 months later...

For anyone else coming across this, here's a post that shows how to fetch data and then create your animations.

 

 

 

  • Like 1
Link to comment
Share on other sites

Here is the code I used that worked with NextJS:

(however, this didn't work for me in a React app)...any ideas?
 

import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);

import data from '../data'

export default function ReactComponent () {
  
//GSAP section
  const revealsRef = useRef([]);
  revealsRef.current = [];

  useEffect(() => {
    revealsRef?.current.map((el, index) => {
      gsap.fromTo(
        el,
        {
          autoAlpha: 0
        },
        {
          duration: 1,
          autoAlpha: 1,
          ease: "power2",
          scrollTrigger: {
            id: `section-${index + 1}`,
            trigger: el,
            start: "top center+=300",
            end: "bottom top",
            toggleActions: "restart none none reverse",
            // markers: "true",
            scrub: "true"
          }
        }
      );
    });
  }, []);

  const addToRefs = (el) => {
    if (el && !revealsRef.current.includes(el)) {
      revealsRef.current.push(el);
    }
  };
  //end of GSAP section
  
  
  return (
	<>
	{data.map((item) => {
	return (
      <div ref={addToRefs} key={item.id}>
      	...
      </div>
    )
  }
    </>
    )
)
}

 

Link to comment
Share on other sites

What didn't work? There should be no difference between React and NextJS. Do you have codesandbox showing the issue?

 

 

Link to comment
Share on other sites

I'm having a hard time getting that to even load, but I think part of the problem is that you are using TypeScript syntax in JavaScript files. Try changing the files to TypeScript extensions, or get rid of the TypeScript syntax, like the ? marks.

 

// TypeScript
revealsRef?.current.map

// JavaScript
revealsRef.current.map

 

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