Jump to content
Search Community

How important is scoping?

NickWoodward test
Moderator Tag

Recommended Posts

In the react learning resources it has a section on context safe animations and interaction: https://gsap.com/resources/React/#animating-on-interaction-
I'd just noticed that scoping is often referred to in the same breath as unsafe animations. Wanted to make sure my understanding is correct: Scoping the selectors is just to avoid targeting the wrong element, right? Whereas "context safety" is to do with ensuring your animations are recorded and cleaned up, which is potentially a more significant problem if missed? Context safety isn't dependent on scoping in any way is it?

Thanks,

Nick

Edit: Also, is this context safe?  https://stackblitz.com/edit/gsap-react-basic-f48716?file=src%2FApp.js

Link to comment
Share on other sites

Hi,

 

You shouldn't have to worry about scoping with contextSafe because the scope being used for the selectors you passed in the useGSAP hook is the same. Take this for example:

const { contextSafe } = useGSAP(() => {}, {
  scope: container // React ref
});

So we're passing a scope that is actually a ref. Then in a specific method you do this:

const myHandler = contextSafe(() => {
  gsap.to(".box", {
    x: 200,
    duration: 1,
    stagger: 0.1,
  });
});

That will basically target all the elements with the class box inside the scope you passed in the useGSAP hook, so it would be like this:

gsap.to(container.current.querySelectorAll(".box"), {
  ...
});

Finally you're right about contextSafe and recording GSAP instances for cleaning up. Under the hood what the useGSAP hook is doing is creating a GSAP Context instance, so contextSafe adds the GSAP Tweens/Timelines and other instances to that particular GSAP Context instance, so that's why it uses the same scope and everything gets reverted when the component is unmounted.

 

Hopefully this clear things up.

Happy Tweening!

Link to comment
Share on other sites

Hi Rodrigo, thanks for the detailed reply! Had to read it a few times but I think I got it now

What I was more wondering though was what if I *don't* provide a scope to useGSAP?  Doesn't scoping prevent me from using elements further up the DOM as ScrollTrigger triggers?

So for example Box's animations can't be triggered by section-1 if I scope it, because section-1 is its parent. Right?

<div>
  <Section>
    <div></div>
  </Section>
  <Section className="section-1">
    <Box></Box>
  </Section>
  <Section>
    <div></div>
  </Section>
</div>
// Box.tsx
//..
useGSAP(() => {
  const boxTl = // some irrelevant paused tl
  ScrollTrigger.create({
    trigger: ".section-1",
    start: "top 80%",
    onEnter: () => boxTl.play(),
  });
}); // scoping here would prevent selecting '.section-1' above?

 

 

 

Link to comment
Share on other sites

16 hours ago, NickWoodward said:

So for example Box's animations can't be triggered by section-1 if I scope it, because section-1 is its parent. Right?

You're right but you can always use a ref for that:

<Section className="section-1" ref="section">
  <Box></Box>
</Section>
useGSAP(() => {
  const boxTl = // some irrelevant paused tl
  ScrollTrigger.create({
    trigger: section.current,
    start: "top 80%",
    onEnter: () => boxTl.play(),
  });
}, {
  scope: section,
}); 

Hopefully this helps.

Happy Tweening!

Link to comment
Share on other sites

51 minutes ago, Rodrigo said:

You're right but you can always use a ref for that:

<Section className="section-1" ref="section">
  <Box></Box>
</Section>
useGSAP(() => {
  const boxTl = // some irrelevant paused tl
  ScrollTrigger.create({
    trigger: section.current,
    start: "top 80%",
    onEnter: () => boxTl.play(),
  });
}, {
  scope: section,
}); 

Hopefully this helps.

Happy Tweening!

but isn't that `useGSAP` code in the Box component? or am i then passing `section`  to `Box`?

Link to comment
Share on other sites

Yeah I missed that, sorry.

 

That is a more convoluted case, in React  child components are rendered before their parents so the element with the section class won't be rendered when that runs.

 

If I was you I'd avoid this setup and keep everything inside the same component in order to make your life easier. If you can't then this is more related to how these type of situations are handled in React rather than a GSAP related issue or even a scoping issue TBH.

 

Happy Tweening!

Link to comment
Share on other sites

10 minutes ago, Rodrigo said:

Yeah I missed that, sorry.

 

That is a more convoluted case, in React  child components are rendered before their parents so the element with the section class won't be rendered when that runs.

 

If I was you I'd avoid this setup and keep everything inside the same component in order to make your life easier. If you can't then this is more related to how these type of situations are handled in React rather than a GSAP related issue or even a scoping issue TBH.

 

Happy Tweening!

by 'keep everything inside the same component' do you mean the trigger/reference to '.section'? so i want to trigger animations using the elements themselves (or children), not anything higher in the DOM, right?

 

Link to comment
Share on other sites

You have a Section component like this:

<Section>
  <Box />
</Section>

Instead of that just have everything inside that Section component and create the ScrollTrigger instance inside that component. Creating the instance in the child component, while needing or using a selector from a parent component is a far more complex setup that you should avoid if possible, instead of having the code in the Box component have the code in the Section component.

 

Happy Tweening!

Link to comment
Share on other sites

yeah, my <Section> components are just dumb reuseable components that accept params for styling and children, so i don't think that would be possible?

my top level looks like this:

<Section>
  <Hero />
  <BlahBlah />
</Section>
<Section>
  <Intro />
  <BlahBlah />
</Section>

tbh i like having a flattened structure like this - It's nice for layout/grid styling using tailwind arbitrary values. lets you see all the major element positions and the grids they sit in, at every breakpoint, in one relatively short file. it's also nice in terms of reuseability.

I guess I'm asking how much more complex is it, so that I can balance that against other design considerations? If it's just a matter of having to be more specific with selectors, i'm ok with that.
 

Link to comment
Share on other sites

Yeah this is mostly a React related thing and not a GSAP related one. We need to keep our focus on GSAP related issues.

 

You should get more knowledge on react components, components communication and state management, all react features that fall outside the scope of these free forums. If you have a specific GSAP related question we'd be happy to look into that.

 

Good luck with your project!

Happy Tweening!

Link to comment
Share on other sites

15 hours ago, Rodrigo said:

You should get more knowledge on react components, components communication and state management, all react features that fall outside the scope of these free forums. If you have a specific GSAP related question we'd be happy to look into that.

 

Not sure how this is related to be honest? I don't think having a reusable top level component is unusual in react, and flattening the structure at the top level seems like a weird thing to call out as a skill issue in a thread about scoping animations.

But even if it was, this is quite a blunt way of responding to a post which essentially just asked about the downsides of not scoping an animation. And it followed an equally blunt reply mistakenly telling me not to comment in an additional topic, despite the comment coming before the topic itself.
 

If I've done something to offend, or I've come across as a leech of the forum, let me know. I appreciate the work and help people give here and I'm aware that it's free - that's why I try to support the place with a premium subscription I've never used.

I appreciate the help you've given me here and in the past too, but there's obviously an issue here, so probably best if we just leave it from now on.

Thanks,

Nick

 

Link to comment
Share on other sites

Hey Nick!

 

Yesterday was a particularly busy day in the forums, so sorry if this came across as blunt. I'm pretty Rodrigo meant nothing by it.

We struggle quite a bit over here with users asking for support with React related issues. React themselves don't have a forum, despite being so widely used and funded by facebook 🫠 Go figure. So it often falls on our very small team to try and help.

 

We're not really React experts, but as it's so prevalent we've tried to learn so that we can help.  We've pretty much outlined ALL of our knowledge in these guides
https://gsap.com/resources/React/

 

We've also created the hook to try and help. We'll happily answer questions about how the hook works, or how cleanup works, or even about the GSAP API itself.

But architectural questions about the best way to structure things in a particular framework involve a lot of domain knowledge, understanding of the codebase in question and a lot of higher level thinking. It's really not GSAP related at that point. That's why we suggest the reactiflux community in our react guide. That's a good place to have discussions about structure.
https://www.reactiflux.com/


As Rodrigo said earlier on

Quote

If I was you I'd avoid this setup and keep everything inside the same component in order to make your life easier. If you can't then this is more related to how these type of situations are handled in React rather than a GSAP related issue or even a scoping issue TBH.

That sums it up really. We advise keeping it simple and if you can't it may be a good idea to talk to some React experts about how best to structure things.

Sorry for coming off as rude, and sorry we can't help more here!

Link to comment
Share on other sites

Hi Cassie,

Sure, and I understand that, but I wasn't really asking for - and certainly wasn't expecting or felt entitled to - react tips or advice. Was just hoping someone could outline the downsides of not scoping useGSAP so that I could make design decisions based on the info available to me ::shrug::.

If that's not possible or is particularly complicated to explain here to someone that doesn't understand that's (obviously) absolutely fine too.

Appreciate the reply though, thanks :)

Link to comment
Share on other sites

I feel like Rodrigo attempted to address that. It's not really about scope at that point. Communicating UP the React tree is into ref land/passing callbacks and stuff. Unless I'm misunderstanding the question. Very possible, React makes my brain hurt.

Quote

Instead of that just have everything inside that Section component and create the ScrollTrigger instance inside that component. Creating the instance in the child component, while needing or using a selector from a parent component is a far more complex setup that you should avoid if possible, instead of having the code in the Box component have the code in the Section component.


I think that's what he meant by this

Quote

You should get more knowledge on react components, components communication and state management, all react features that fall outside the scope of these free forums. 

It's not a skill level callout, just advice on what to look into to find a solution


Have you seen the component communication sections in the docs? Maybe there's something there that can help?
https://gsap.com/resources/react-advanced#component-communication

Link to comment
Share on other sites

57 minutes ago, Cassie said:

I feel like Rodrigo attempted to address that. It's not really about scope at that point. Communicating UP the React tree is into ref land/passing callbacks and stuff. Unless I'm misunderstanding the question. Very possible, React makes my brain hurt.

I mean I could easily be wrong, but I didn't think I was asking about communicating up the react tree?

I'd thought I was just asking about having a child component's gsap animation reference a parent - which may not work in React without passing refs, but it *does* work with Gsap, React and Astro.
Maybe this is a side effect of SSR and astro rendering html prior to react components, and gsap *shouldn't* be able to select and use parent elements as triggers, but it seems to be able to. And that seemed linked to the idea that you scope your gsap selectors. I was just asking if it's ok to not scope the selectors and allow a child element to keep using the parent as a trigger - which it's currently doing

I do understand about not talking about frameworks, but by avoiding talking about astro and react... I've got into a conversation about the best way to structure a framework that I'm not using?
:DJust as a takeaway from this: Is it fair then to say that ScrollTrigger animations must be placed at *at least* the level of the component that contains the trigger?

 

57 minutes ago, Cassie said:

It's not a skill level callout, just advice on what to look into to find a solution

And I would've written it off as a bit of a curt and roundabout way of saying 'do more work' - which is fine, people need/deserve that sometimes when asking questions - if it wasn't in reply to a post where I outlined how and why I structured my app in the way I did, or for the other message that came off pretty condescending too. Hard for "you need to learn more" to come off as more than a skill issue in that context.

Either way it doesn't really matter. Meant or not it was just a "let's stop" reply on my part and a brief explanation that I don't take this forum for granted. The replies annoyed me a bit, sure, but it's not a big deal.

  • Like 1
Link to comment
Share on other sites

Quote

I'd thought I was just asking about having a child component's gsap animation reference a parent - which may not work in React without passing refs, but it *does* work with Gsap, React and Astro.

Ok, I get what you're after, sorry that took a while. I got confused because you're talking about referencing a parent, which isn't really what's happening. At least not *just* directly referencing a parent.


By not scoping your useGSAP hook your selectors are global. So they'll grab anything that's available to them in the DOM. Not just the parent. Literally anything that matches that selector that React has already rendered.

Use at your own risk I guess. It's fine if you feel you've got a reliable idea of what's been rendered at the point that GSAP code runs, and if you're using selectors wisely, but it's not really the correct "React" way of doing things. (which is not a discussion to get into here 😂)
 

Quote

Just as a takeaway from this: Is it fair then to say that ScrollTrigger animations must be placed at *at least* the level of the component that contains the trigger?

It's more "reacty" to keep your GSAP code in a parent and use a scope so that your selectors aren't global. Then you can drop your components anywhere without worry, which is kinda the point of React. Encapsulation.

But you do you really. If you want to take the guardrails off and just make all your selectors global you can!

Link to comment
Share on other sites

3 hours ago, Cassie said:

By not scoping your useGSAP call your selectors are global, yeah. So they'll grab anything that's available to them in the DOM. Not just the parent though. Literally anything that matches that selector that React has already rendered.

3 hours ago, Cassie said:

It's more "reacty" to keep your GSAP code in a parent and use a scope so that your selectors aren't global. Then you can drop your components anywhere without worry, which is kinda the point of React. Encapsulation.

But you do you really. If you want to take the guardrails off and just make all your selectors global you can!

 

It's not what I want to do particularly, but it's how I might have to I guess? That's exactly what I was double checking with the selectors though, so thanks! 

Re being more reacty & encapsulation - definitely, you're right, reaching outside the component isn't reacty. But I mean that gsap selector is a little bit magic/non reacty, I've just confused SSR/astro magic with gsap magic I guess? I did try to clarify if I should make the elements themselves (in this case the Box) the trigger instead though. So the animation, ScrollTrigger and trigger element would all be in the Box. Sound right?

I'm just not sure about having to place the Box's animations in Section, just to have access to the trigger element? That doesn't feel like the Box is very well encapsulated either now? Rather than moving its animations to its parent and the trigger element, couldn't I just change what triggers the animation? Which the more I type about it feels like it must just be the answer?
 

But yeah, no structure talk so no worries if that's too much to get into! 😄

Link to comment
Share on other sites

The React way is always something that will lead to discussions and disagreements no matter what. I've worked with react for over 8 years, ranging from small static websites to large SaaS apps with hundreds of files and convoluted state management. At the end you have to decide what's more important the so called component encapsulation and abstraction or a more flat and simpler architecture that makes other tasks easier. Unfortunately we can't have both.

 

On that subject, what is understood with encapsulation? How is not secure if you don't abide to it? How can it lead to issues? Those are not general concepts and cannot be applied in the same way to every project in react, that is something that has to be dealt with on a project basis. Is your project handling any information that needs an extra security layer? If not then the whole argument that component encapsulation could lead to security problems is out the window. Is your project mostly static in terms of what is rendered during the app's life cycle? Then what's the point of having hundreds of refs, use forwarding refs and imperative handlers, if you know that the selectors you're pointing to are going to be there no matter what? See how deep this rabbit hole gets?

 

I'm not a big fan of the react way TBH (is worth mentioning that I'm not entirely against it either) since I've seen it create some confusion from time to time. I'm a believer that these type of decisions need common sense as well as pre-set guidelines. For example in this forum everyone with some experience will tell you: "animate scale instead of height and width because you get better performance". But there are some projects and situations when that is not an option, so you have to use width and/or height. In those cases be sure to make the elements as small as possible and that they affect the document's flow in the most minimal way. The dogmatic approach is to always animate transforms but the realistic one is that sometimes there is no other option and when is done properly you'll get a nice smooth animation that won't hurt UX. Again is a project by project approach not something set in stone. A project in react is no different. Sure component abstraction in order to keep everything as small as possible and make it easier to maintain, is the desired outcome, but if that will imply that you'll have to code through a bunch of hoops in order to get things working the way you want, you might want to re-visit that approach and maybe in a few cases/components, just have a flatter structure with a bit more code in those components in order to make code simpler and easier to maintain.

 

The only rule of thumb I follow no matter what is this: "Test, test, test and then test more". If you break encapsulation and you're not doing things the react-way, but the app works the way you want/need, nothing breaks and development and maintainability is easy, then what's the problem?

 

In this case you have to decide based on what works. Keep everything the way you have it now and find a way to tell the child component that is safe to use a specific selector and only then create your GSAP instances? Somehow pass the parent ref to the child component and use that as the scope in order to create the GSAP instances when that ref is not null/undefined? Flatten your structure in order to make it simpler? I was adamant to a flatten structure because in my experience that is always the best road in the long term, nothing more, but this is your project and your time, hence your decision. I just offer my personal opinion based on my experience, but I don't intend to enforce anything on anyone here or anywhere else. If somehow it felt that way I'm sorry about it and trust me, it was never my intention.

 

Finally if in some way I was rude, blunt, condescending or insulting to you, I apologize for that as well, it wasn't my intention neither. The GSAP forums have been for over a decade a safe place where nobody gets called names, downvoted or anything of the sort. Is a healthy community that aims to give the best possible advice with the time we have in our hands, while making everyone welcome and transmitting the vibe that you're safe here. If I failed in that way, then there is a learning experience for me, for sure. The aim is to be better than I was yesterday.

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

2 hours ago, Rodrigo said:

At the end you have to decide what's more important the so called component encapsulation and abstraction or a more flat and simpler architecture that makes other tasks easier. Unfortunately we can't have both.

And I don't have a problem with that at all. But I can't make the choice if I don't know the choice is there to be made 🤷‍♂️ - especially when rightly or wrongly, things like abstraction are pretty big in react. I wasn't here to argue in favour of either approach, outside of the limitations my current-app choices have obviously imposed. I'm certainly not wedded to them, but to some extent I do have to work round the project as it exists today

I guess that was part of the reason for my reply to you: It felt a bit harsh to be directed to learn fundamental react concepts because of that structure? I showed it just to give an idea of my app's layout - if you'd said "sections can't be abstract or dumb in this scenario" I'd have asked if I could wrap it and include the animation there, or for a small example of how you'd structure it, for example.

I admit my question was initially poorly formed - I missed the cleanup return function and that the selector shouldn't have been working at all - so I can understand it was probably a frustrating question. Sorry for that. If not, it was a misunderstanding on my part, but from my perspective it came across that way. I appreciate the apology either way, and don't think it intentional now - but worth checking because it at least appeared as if I genuinely had annoyed you. In fact I think the surprise added to my response - because your description of this forum is accurate. It is a welcoming place, you're right
 

2 hours ago, Rodrigo said:

In this case you have to decide based on what works. Keep everything the way you have it now and find a way to tell the child component that is safe to use a specific selector and only then create your GSAP instances? Somehow pass the parent ref to the child component and use that as the scope in order to create the GSAP instances when that ref is not null/undefined? Flatten your structure in order to make it simpler? I was adamant to a flatten structure because in my experience that is always the best road in the long term, nothing more, but this is your project and your time, hence your decision.

1:  As the parent to Box will the `<Section>` component ever not be available though, outside of the first render? (even that still seems to be - think that's SSR magic)
2: Passing a ref of Section to Box for scope? - again, maybe ignorance, but I'm not too sure why this would get much more complicated?
3: I'm not entirely sure what you mean by flattening in practical terms though?

Wasn't #1 sort of the question I was asking? That there might be more to think about than simply omitting scope? #2 I agree, feels overly complex passing the parent to the child (and I just don't like it), and 3 requires a bit of a rework for me, or placing the animations in the astro file - which I've had some bad experiences with when using react. 3 I guess could be achieve by wrapping the dumb section component with another component too, but I'm guessing you wouldn't be a fan.

Anything wrong with just moving the trigger to become the element itself and tweaking the start/end properties? Because that seems like the best of both right now


 

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