Jump to content
Search Community

Using GSAP with react on mapped components to toggle them onClick

mabdelra test
Moderator Tag

Go to solution Solved by GreenSock,

Recommended Posts

Hi guys im trying to animate an FAQ component in react the animation works in my localhost however when i initiate a build and host it on netlify it no longer works and the faq stays in its expanded state, im using tailwindcss for the styling if you want to copy the code directly, sorry if this is messy im still unfamiliar with using codepen to provide a demo especially for react since im only starting to learn it now 😕.

 

import { useEffect  } from "react"
import { useRef } from "react"
import gsap from "gsap"
import SplitText from 'gsap/SplitText'
gsap.registerPlugin(gsap, SplitText)


const questions = [
    {
        title: 'question?',
        answer: ' Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis modi qui non ullam hic maxime veritatis et nemo eius nihil eos rem, asperiores nulla aspernatur quisquam eaque officiis atque odit?',
        id: 1,
    },
    {
        title: 'question?',
        answer: ' Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis modi qui non ullam hic maxime veritatis et nemo eius nihil eos rem, asperiores nulla aspernatur quisquam eaque officiis atque odit?',
        id: 2,
    },
    {
        title: 'question?',
        answer: ' Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis modi qui non ullam hic maxime veritatis et nemo eius nihil eos rem, asperiores nulla aspernatur quisquam eaque officiis atque odit?',
        id: 3,
    },
    {
        title: 'question?',
        answer: ' Lorem ipsum dolor sit amet consectetur adipisicing elit. Nobis modi qui non ullam hic maxime veritatis et nemo eius nihil eos rem, asperiores nulla aspernatur quisquam eaque officiis atque odit?',
        id: 4,
    },
]



const Faq = () => {
    const app = useRef([])

    useEffect(() => {
        return () => {
            let active
            let faq = document.querySelectorAll('.question');

            faq.forEach((element) => {

                let answer = element.querySelector('.answer')
                let animation = gsap.timeline({})
                .to(answer, {
                    opacity: 0,
                    visibility: 'invisible',
                    duration: 0.05
                })
                .to(answer, {
                    height: 0,
                    duration: 0.4,
                    ease: 'power3.inOut'
                },'<+100%')

                element.animation = animation
                element.addEventListener('click', function(){
                    if(active){
                        active.animation.play() // reverse (close) active element's animation
                    }
                    
                    element.animation.reverse() // play (open) the clicked element's animation
                    active = element
                })
            })
        }
    })

    return(
    questions.map(item => {
        const {title,id, answer} = item;
        
        return (
        <div className="question"  key={id} ref={(index, element) => app.current.splice(index, 1, element).push(element)}>
            <div key={id}  className='mb-5  border text-white border-line-rule  hover:border-btn-clr hover:text-btn-hvr hover:transition-[350ms] hover:bg-btn-bg rounded-xl py-4 px-4 flex flex-col items-center justify-between'>
            <button className='closeBtn flex  py-2 justify-between items-center 2xl:text-2xl 2xl:py-2.5 w-full'>
                <h3>{title}</h3>
                <div className='close'>
                <svg data-accordion-icon className="w-6 h-6 shrink-0" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path  d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" ></path></svg>
                </div>
            </button>

            <div className='answer border-t border-line-rule'>
                <div className='py-5'>
                    {answer}
                </div>
            </div>
        </div>
    </div>)
    })
    )
}

export default Faq

 

 

 

Link to comment
Share on other sites

Thanks for being a Club GreenSock member! 🥳

 

I notice several mistakes:

  1. You're not actually doing anything in your useEffect() - you're just returning a cleanup function that has all the logic in it. 
  2. You didn't use an empty dependency array for your useEffect(), so it'll get called on every render.
  3. You're not doing proper cleanup. I'd highly recommend using gsap.context() to make that easier. 

So it's mostly React-specific stuff. I'm not much of a React guy myself, but I'd strongly recommend reading this article: 

 

Here's a very quick CodePen that I slapped your code into in case it helps: 

See the Pen gOKppmm?editors=0010 by GreenSock (@GreenSock) on CodePen

 

Good luck! 👍

Link to comment
Share on other sites

21 hours ago, GreenSock said:

Thanks for being a Club GreenSock member! 🥳

 

I notice several mistakes:

  1. You're not actually doing anything in your useEffect() - you're just returning a cleanup function that has all the logic in it. 
  2. You didn't use an empty dependency array for your useEffect(), so it'll get called on every render.
  3. You're not doing proper cleanup. I'd highly recommend using gsap.context() to make that easier. 

So it's mostly React-specific stuff. I'm not much of a React guy myself, but I'd strongly recommend reading this article: 

 

Here's a very quick CodePen that I slapped your code into in case it helps: 

 

 

21 hours ago, GreenSock said:

 

Good luck! 👍

Thank you so much for the fast response! i see what you mean and it works perfectly :)

 

I do have one more question though, how do i go about adding another onClick event that closes the modal when its clicked for the second time?

 

I really appreaciate how helpful you guys at Greensock are by far the best community I have ever been a part of!

Link to comment
Share on other sites

1 hour ago, mabdelra said:

I do have one more question though, how do i go about adding another onClick event that closes the modal when its clicked for the second time?

 

There are many ways to handle that. Here's one: 

See the Pen JjZdaMB?editors=0010 by GreenSock (@GreenSock) on CodePen

 

1 hour ago, mabdelra said:

I really appreciate how helpful you guys at GreenSock are by far the best community I have ever been a part of!

We love hearing that. Thanks so much! 💚

Link to comment
Share on other sites

Hi Jack, Thanks again for your help!

 

I am quite lost at the ._ notation syntax you are using in the if statements and the clean up function can you explain what that is and how come you dont need to declare the function using a const or let statement?

 

Lastly why do we need to clean up the event listeners at the end of the context function ?

 

Thank you so much again I really appreciate it!

Link to comment
Share on other sites

  • Solution
6 hours ago, mabdelra said:

I am quite lost at the ._ notation syntax you are using in the if statements and the clean up function can you explain what that is and how come you don't need to declare the function using a const or let statement?

I'm just assigning arbitrarily-named properties to the elements themselves. Elements are just objects anyway, so you can do that. For example: 

let obj = {}; // create an object

obj.myProperty = 100; // assign a property

let el = document.querySelector("#id"); // get Element

el.myProperty = 100; // assign a property

I just use an underscore as a convention to indicate it's a "private" property (not to be edited by other developers). It's merely a convention. You can name it whatever you want. Omit the _ if you want. 

 

6 hours ago, mabdelra said:

Lastly why do we need to clean up the event listeners at the end of the context function ?

That'll get called anytime the context gets reverted. So when your useEffect() triggers its cleanup, like maybe if the component gets unmounted/trashed. If you don't cleanup your event listeners, they could keep getting triggered but honestly that's unlikely in our scenario because the elements wouldn't be in the DOM anymore thus they wouldn't get clicked but it's just considered good practice to always remove your event listeners in the cleanup function. 

 

Does that clear things up? 

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