Jump to content
Search Community

ScrollTrigger breaks React Router?

Tonycre8 test
Moderator Tag

Recommended Posts

Hello GSAP gang, I have a little bit of a situation going on.

I understand that there's already a post on this topic, but I need help with this one as well.

I've got some code that I have tried out but it seems that I've been unable to fix this issue. The coding sandbox comes up with an error: "Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."
https://codesandbox.io/s/react-router-re-render-8btmz?file=/src/index.js

I was wondering if any of you guys had any experience in this, and how to get it so that the React router link works as intended?

Thanks

Link to comment
Share on other sites

Hi @Tonycre8,

 

I didn't have a change to dive into this, yet, but at a glance: Make sure you're doing garbage cleanup on your ScrollTrigger too (https://greensock.com/docs/v3/Plugins/ScrollTrigger/kill()), via ScrollTrigger.getById('yourID').kill();. Also, you're still using the old gsap2 syntax (you not longer need to use TimelineLite, etc) you can now use cont slide = gsap.timeline();

 

  • Like 1
Link to comment
Share on other sites

Hey there. Thanks for the response.

10 minutes ago, elegantseagulls said:

Hi @Tonycre8,

 

I didn't have a change to dive into this, yet, but at a glance: Make sure you're doing garbage cleanup on your ScrollTrigger too (https://greensock.com/docs/v3/Plugins/ScrollTrigger/kill()), via ScrollTrigger.getById('yourID').kill();. Also, you're still using the old gsap2 syntax (you not longer need to use TimelineLite, etc) you can now use cont slide = gsap.timeline();

 

I'll come back to updating the syntax in a short while, but about the potential solution.

What would the ID be? I've tried using this as well as the other bits of cleanup I've done.

ScrollTrigger.kill() 

in my code since then, but it hasn't seemed to work either.

Link to comment
Share on other sites

We seemed to have found a fix! Well, my brother worked on it whilst I was waiting for someone to turn up for a meeting.

Based on a comment from here: 

We found that the issue is actually just adding a surrounding div to my home.js code, so something like this:
 

return (
    <div>
      <div className="sections">
        <section className="section" style={{ background: "white" }}>
          <div>
            <Link to="/blog">Link</Link>
          </div>
        </section>
        <section
          className="section"
          id="section2"
          style={{ background: "green" }}
        >
          <p>Section 2</p>
        </section>
        <section
          className="section"
          id="section3"
          style={{ background: "blue" }}
        >
          <p>Section 3</p>
        </section>
        <section
          className="section"
          id="section4"
          style={{ background: "orange" }}
        >
          <p>Section 4</p>
        </section>
        <section
          className="section"
          id="section5"
          style={{ background: "yellow" }}
        >
          <p>Section 5</p>
        </section>
      </div>
    </div>
  );

Although we are still unsure of why this works, it seems to be a working solution.

  • Like 1
Link to comment
Share on other sites

  • 2 weeks later...

Hi @Tonycre8

 

GSAP needs to do some cleanup before the component gets removed from the DOM, so you should create your scroll triggers inside useLayoutEffect like this. There should be no need to add an extra div using this method.

 

useLayoutEffect(() => {
  
  let st = ScrollTrigger.create({
    pin: true,
    ...
  });

  return () => {
    st.kill();
  };
}, []);

 

 

 

  • Like 2
  • Thanks 2
Link to comment
Share on other sites

  • 4 months later...
16 minutes ago, OSUblake said:

Welcome to the forums @spirconi

 

Can you make a demo on CodeSandbox showing the problem? And did you try useLayoutEffect and make sure the component you are pinning is not fragment?

 

 

I have not tried to make a demo yet, I was hoping one existed I could try to work it out from first. 

 

Actually, I tried useLayoutEffect, and whilst it did not work, it may provide a clue. 

 

With useEffect, the pins work fine on initial load. The issue is when i go to / come back from another page it just ignores the pinned scrolltriggers. I notice the markers work as expected. I can see the top marker stay in place, which is met by the element start marker. When i come back to the page, the markers are in the same place but are just ignored. 

 

I noticed with useLayout effect the top marker moves up the page, so the pin never triggers. So that didnt work. 

 

I am using locomotive scroll setup (all works as expected minus this issue). I am suspecting it may be something to do with that setup up rather than gsap specifically. 

Link to comment
Share on other sites

On 10/23/2021 at 12:34 AM, spirconi said:

Ok, I added the following to the parent component which seems to have done the trick. 

 

useEffect(() => {
ScrollTrigger.refresh();
}, [ScrollTrigger.getAll()]);

Here to report that I had the exactly same issue and this code solved it. Navigating to another page and back breaks scroll triggers.

Link to comment
Share on other sites

1 hour ago, hereandnow said:

Navigating to another page and back breaks scroll triggers.

 

Are you still having trouble?

 

Just an FYI about the code posted above. It will run on every render and is basically the same as having an effect with no dependencies .

 

// has the same effect
useEffect(() => {
  ScrollTrigger.refresh();
}, [ScrollTrigger.getAll()]);

// as this
useEffect(() => {
  ScrollTrigger.refresh();
})

 

This is due to way React detects changes using Object.is. 

let same = Object.is(ScrollTrigger.getAll(), ScrollTrigger.getAll())

console.log("SAME", same) // false

 

 

Link to comment
Share on other sites

In my instance it is a very light app and only re rerenders twice as there is not much interactivity. I needed a fast solution to get the site live, but noted this is not recommended and will look for a better solution once I have chance to go back to it. 

  • Like 1
Link to comment
Share on other sites

Hi guys. 

 

I can report having the same error on route change of a Next.js app (Next Router, React 17.0.2, Next 11.1.2, GSAP 3.8.0).

It doesn't occur on a forced reload, just when navigating away from the page via Next router.

 

If I removed the Pinning, error went away. Note, I was using Refs for all dom targeting, and a custom useLayoutEffect hook I wrote for Next (which only runs Isomorphically). The hook had cleanup deps defined and killing ScrollTrigger on unmount didn't prevent the error.

 

In the end, the fix was to ensure the target ref was NOT on the component's parent element. The target ref seems to need a wrapper / parent so the injected pin spacers are actually child elements of the component. 

 

Interestingly, this also improved render times when navigating to the page. 

 

Still investigating specifics.

 

 

 

 

 

 

  • Like 2
  • Thanks 2
Link to comment
Share on other sites

  • 2 months later...

Wrapping the scrolltrigger pin in a div didn't work for me as it seemed to change the scrolltrigger progress values?
Killing scrolltrigger on useEffect/useLayoutEffect unmount also didn't work.
What worked was manually triggering kill() before unmounting the component. 
So something like this...
 

    const backBtnPress = () => {
        animationsRef.current.menuFadeIn.vars.onReverseComplete = () => {
            sTrigger.current.kill();
            setNav('more')
        }
        animationsRef.current.menuFadeIn.reverse()
    }

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

  • 3 months later...

Using VueJS, adding the following to the router fixed the issue:
 

router.beforeEach((to, from) => {
  if (from.path !== to.path) ScrollTrigger.getAll().forEach(el => el.kill())
  return true
})

router.afterEach((to, from) => {
  if (from.path !== to.path) { ScrollTrigger.refresh(); ScrollTrigger.update() }
})

 

  • Like 2
Link to comment
Share on other sites

  • 10 months later...

Hello, in my case the problem was when I load my react website, the initial current component which refreshes it is fine and the scroll position is also good but after we navigate to other components their I have images which load after I navigate to that component because of which scroll trigger position fails to load the right position as it doesn't refresh after the image loads.

So, what I did is in react, there is an attribute called onLoad which gets triggered after the image loads.

Hence, onLoad={()=>ScrollTrigger.refresh()} solved my problem.

  • Thanks 1
Link to comment
Share on other sites

Hi @Arman_ and welcome to the GreenSock forums!

 

It's pretty tough to troubleshoot without a minimal demo - the issue could be caused by CSS, markup, a third party library, your browser, an external script that's totally unrelated to GSAP, etc. Would you please provide a very simple CodePen or CodeSandbox that demonstrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best (avoid frameworks if possible). See if you can recreate the issue with as few dependancies as possible. If not, incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, then at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

Here's a starter CodePen that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

 

If you're using something like React/Next/Vue/Nuxt or some other framework, you may find StackBlitz easier to use. We have a series of collections with different templates for you to get started on these different frameworks: React/Next/Vue/Nuxt.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

Link to comment
Share on other sites

  • 9 months later...

For anyone stumbling upon this thread in the future, there's no more need to use useLayoutEffect() and do cleanup manually like that - you can just use the useGSAP() hook which does all that for you. 

 

Also, regarding the need to add a wrapper <div>... I suspect that just has to do with the fact that whenever you pin an element, ScrollTrigger must fabricate a NEW <div> as a wrapper around that original element so that it can act as a placeholder when the "real" element gets set to position: fixed and is removed from the document flow. So if that pinned element is technically the root element of your component, maybe React is complaining because there was a new element injected there. That's easy to remedy by creating your OWN wrapper <div> that you then give ScrollTrigger as the pinSpacer so that ScrollTrigger doesn't need to do any creation of a new <div> and injecting it into the DOM: 

{
  trigger: ".pinned-element",
  pinSpacer: pinSpacerRef.current, // <-- your wrapper around .pinned-element
  ...
}

For more on using GSAP in React, see https://gsap.com/react

  • Like 2
Link to comment
Share on other sites

  • 1 month later...
On 3/9/2023 at 10:19 AM, Arman_ said:

Hello, in my case the problem was when I load my react website, the initial current component which refreshes it is fine and the scroll position is also good but after we navigate to other components their I have images which load after I navigate to that component because of which scroll trigger position fails to load the right position as it doesn't refresh after the image loads.

So, what I did is in react, there is an attribute called onLoad which gets triggered after the image loads.

Hence, onLoad={()=>ScrollTrigger.refresh()} solved my problem.

same problem here, I suspected that this is happening but didn't know that `onLoad` exists, so I tried it and it worked perfectly.
So, I made an account just to thank you🙇‍♂️

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