Jump to content
Search Community

Need some help with a react accordion

Ehrgein test
Moderator Tag

Go to solution Solved by Rodrigo,

Recommended Posts

Hey there! First of all, merry christmas. I'm a little new to GSAP and I've been trying to make an accordion for my portfolio. So far my code "works" but I KNOW it's EXTREMELY convoluted/messy, so I was wondering if anyone could help me 'refactor' it in some way. Or rather, show me how someone would do it properly, than kyou very much! I'm coding it on React along with GSAP.


This is the layout effect that controls the animation timeline.

  const AnimationRef = useRef();
  const ProjectRef = useRef();
  const ProjectRefTwo = useRef();
  const arrowAnimationRef = useRef();

  const [menu, setMenu] = useState(false);

  useLayoutEffect(() => {
    let tl = gsap.timeline();
    tl.from(ProjectRef.current, {
      height: 0,
      ease: Power4.easeInOut,
    });

    const arrowbtn = document.getElementById("arrowbtn");

    if (menu) {
      arrowbtn.addEventListener("click", () => {
        tl.reversed() ? tl.play() : tl.reverse();
      });
    }
  }, [menu]);

  const onRotate = () => {
    setisRotated((rotated) => !rotated);
  };

And here's the JSX: (BsChevronDown is a react-icons icon and IconContext Provider is just to change the colour of the arrow, the isRotated state is controlling a small animation that turns an arrow up or down depending on if the project is open or not.

            <div className="">
              <span className="">KEEP MOVING</span>
              <IconContext.Provider value={{ size: "50px", color: "black" }}>
                <div>
                  {menu ? (
                    <BsChevronDown
                      onClick={onRotate}
                      ref={arrowAnimationRef}
                      id="arrowbtn"
                      className={
                        isRotated
                          ? "project-arrow-down-new"
                          : "project-arrow-down open"
                      }
                    />
                  ) : (
                    <BsChevronDown
                      onClick={() => setMenu(!menu)}
                      className={
                        menu
                          ? "project-arrow-down open ml-1 pepito11 "
                          : "project-arrow-down ml-1 duration-300 pepito40"
                      }
                    />
                  )}
                </div>
              </IconContext.Provider>
            </div>
            <div id="projects">
              {menu ? (
                <div ref={ProjectRef} className="overflow-hidden">
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                  <div>PROJECT TEXT</div>
                </div>
              ) : null}
            </div>

 

Any help I'd greatly appreciate it. If you need any other code or anything please do let me know, I'm not used to sharing my code so I don't know what could be more useful specially when it comes to GSAP.

 

Also, you ll see a "pepito40" class, that was just my way of identifying which arrow was getting render, it doesnt have any css or anything.

 

 

 

Link to comment
Share on other sites

Hi @Ehrgein, welcome to the Forums and Merry Christmas!

 

Is a bit hard for us to see/debug issues without a minimal demo. We have a few starter templates for React:

https://stackblitz.com/@GreenSockLearning/collections/gsap-react-starters

 

This one specifically deals with toggling a timeline instance:

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

 

Of the code snippets you posted there are two things that caught my attention. First the fact that your layout effect hook creates and toggles the timeline at the same time. As you can see in the template, just create the timeline once in a layout effect with an empty array and then toggle it using another layout effect with the boolean dependency. Second, the Chevron conditional rendering seems odd to me. If you want to execute a different code based on one boolean, just do that in the handler, not in the JSX, since based on that conditional logic the class applied when menu is falsy will never be applied since that is rendered only when menu is falsy and the same can be applied for the isRotated boolean. I think is a bit confusing, convoluted and can lead to errors and maintenance issues down the line.

 

Also take a look at this article:

Finally we strongly recommend using GSAP Context when using GSAP in a React app:

https://greensock.com/docs/v3/GSAP/gsap.context()

 

Let us know if you have more questions and remember to add a minimal demo.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

Thank you so much for the explanation Rodrigo! gsap.context() definitely seems to make things easier!


I didn't know about using two different useLayoutEffects, that's awesome to know!

Yes, I wasn't happy AT ALL with the arrow approach, my main problem was that I was having a hard time rendering the text on the click of the arrow, and since the text was rendering conditionally with the arrow, the animation was never triggering since the falsey condition for the ternary was a null div (Hence not being able to animate since there was no ref)

 

Thank you for the playgrounds! 


EDIT: I managed to make it work with a codepen like this: https://stackblitz.com/edit/gsap-react-basic-f48716-kwum8b?file=src%2FApp.js

 

In the codepen it doesnt show, but here's the result dn7GCcX.gif

O

 

I'll definitely be using Context from now on. If you were to recommend me an aproach for doing this with multiple columns, would you recommend doing a timeline for each specific project? I was thinking of say : User clicks on B project while A is open, then the handler for the B one should toggle the timeline for A and the timeline for B? or is there a better approach for this?

Thank you so much by the way! 

  • Like 1
Link to comment
Share on other sites

  • Solution

Hi,

 

Yeah, the issue here is that you should create a component for each accordion item that handles the animation of just that item. In the parent component (the one that holds all the accordion items) create a state property that you can pass as a prop to each element to check which is the active one and toggle the animations based on that.

 

Right now I have a few other examples that I'm working on, so I can't tackle this one, but it should be pretty straight forward since the logic is quite simple.

 

Hopefully this helps.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

1 minute ago, Rodrigo said:

Awesome! It does help a lot, I shouldn't have much of a problem now that I know what to do or what works.

 

I'll make sure to give the docs a further read and keep an eye on for more updates! Thanks a lot!

 

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