Share Posted July 10, 2020 I'm sure this is something I'm doing wrong but I can't seem to accurately update state from an onComplete event. The counter successfully updates once but then seems stuck on '1'. The example below is very simple to show the issue. https://stackblitz.com/edit/react-l6a9q7 Any help would be really helpful Thanks so much! Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 Hey Tom. You just need to pass in the count to the useEffect: useEffect(() => { tl.current.to(test, 2, { opacity: 0, onComplete: update }); }, [count]); Link to comment Share on other sites More sharing options...
Author Share Posted July 10, 2020 Thanks for replying so fast. I tried adding the count but the behaviour is pretty erratic, would you mind taking a look and letting it run 5-6 times?https://stackblitz.com/edit/react-l6a9q7 Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 That's because it will create a new tween every time that the count state updates. Sorry, I don't really use React and this is 100% a React question so I'm probably not the best equipped person to do it If this is all you're doing then you could just use a tween with overwrite: true https://stackblitz.com/edit/react-nempcw With that being said, @OSUblake, @Rodrigo, or @elegantseagulls will probably come by and correct my ignorance. Also FYI there was a recent article made about hooks + GSAP by @Ihatetomatoes that might be useful: https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/ Link to comment Share on other sites More sharing options...
Author Share Posted July 10, 2020 Well you have it working so it's a huge step up from my efforts! I guess if I can store the whole tl in a ref, it won't create a new each time. You've definitely pointed me in the right direction so thanks! Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 Hey Tom, it's Petr here from Ihatetomatoes.net, was the article that @ZachSaucier pointed you to useful? Let me know if I should add anything to the guide. Cheers Link to comment Share on other sites More sharing options...
Author Share Posted July 10, 2020 Hi Petr, I checked out the guide and it was really useful so thank you! Unfortunately, I could not see how to solve the specific issue I had other than how @ZachSaucier kindly showed me. Did you take a look at the first couple of examples to see the issue? Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 Hm I am not 100% sure what effect you are trying to create but when I pass test.current to the tween I don't see any issue with updating the state in the onComplete. https://stackblitz.com/edit/react-bowg4f 1 Link to comment Share on other sites More sharing options...
Author Share Posted July 10, 2020 I think you were checking out the fixed version but Zach wasn't 100% sure it was the best approach. The original 'broken' one can be found here:https://stackblitz.com/edit/react-l6a9q7 Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 Again, I am not sure what effect you are trying to achieve but the link I posted would be my way how to update state after a tween is completed. Hope that helps. Link to comment Share on other sites More sharing options...
Author Share Posted July 10, 2020 That's great, that is the one I have used which was the same method that Zach recommended. I think he was just looking for validation from someone with more React experience so thanks for providing that! i really appreciate how helpful everyone is on this forum. Link to comment Share on other sites More sharing options...
Share Posted July 10, 2020 No problem. I have just changed the way the ref is used in the markup and passed text.current to the tween, that seems to be the right approach according to the official React useRef docs. 2 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 Hey guys, I've been looking into this and I really don't like this part: useEffect(() => { console.log(tl.current); tl.current.to(test.current, 2, { opacity: 0, onComplete: update }); }, [count]); Basically this code tells GSAP to add a new instance to the end of the timeline each time the count value in the component's state is updated, which happens every time the timeline instance is completed. This means that the timeline gets bigger and bigger, which could lead to unwanted results and a memory leak. The instance should be added to the timeline only once in the initial render of the component, using an empty array in the useEffect() hook. I'm trying to figure something out in order to post a solution with that particular code, but I've been interrupted several times by household situations . As soon as I have something concrete to post I'll get back to you. Happy Tweening!!! PS: 46 minutes ago, Ihatetomatoes said: Let me know if I should add anything to the guide. Yeah, I have two suggestions. One, don't use Hooks with GSAP. Two, use Vue or Svelte 3 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 Ok, so I don't know what to tell you besides don't use hooks with GSAP. This is a version using hooks with the approach I mentioned before: https://codesandbox.io/s/gsap-update-state-hooks-4nezn It doesn't work. This is basically the same code using a class component: https://codesandbox.io/s/gsap-update-state-class-tdj1i This does work. I'll ask around in the reactiflux community to see if someone can shed some light into this. Happy Tweening!!! 3 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 Ok, so the issue is stated here: https://reactjs.org/docs/hooks-reference.html#functional-updates Basically since the closure in the initial render can access only the initial state, so you need to explicitly pass the current state to the callback using the functional update approach: const update = () => { setCount(prevCount => prevCount + 1); }; So this sample now works as expected: https://codesandbox.io/s/gsap-update-state-hooks-4nezn And apparently I have to eat my words and you CAN use GSAP with the hooks API Happy Tweening!!! 3 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 What an unintuitive way of handling things... 1 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 Thanks Rodrigo for the code examples. I know most people here prefer the class approach for the more readable code, but I think it's useful to give people a clear way how to use GSAP tween and timeline with both class and hooks approach. If you think there is something missing or not clear enough in this guide please let me know. https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/ I will create more guides for React + GSAP and class approach would definitely be one of them. 2 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 55 minutes ago, ZachSaucier said: What an unintuitive way of handling things... That's hooks! It calls the same function on every render/update, so values won't persist. Only the initial ones. That's another reason why I don't recommend creating animations inside a useRef call. It will create a new animation and then throw it away on every update. export default function App() { // creates a new timeline on every update // keeps the original, and throws away the new one const tl = useRef(gsap.timeline({ paused: true, repeat: -1 })); useEffect(() => { tl.current .to(testEl.current, { duration: 1, opacity: 0, onComplete: update }) .play(); }, []); } Better approach. export default function App() { const tl = useRef(); useEffect(() => { tl.current = gsap.timeline({ repeat: -1 }) .to(testEl.current, { duration: 1, opacity: 0, onComplete: update }); return () => tl.current.kill(); // return function to kill on unmount }, []); } 5 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 1 hour ago, Ihatetomatoes said: Thanks Rodrigo for the code examples. I know most people here prefer the class approach for the more readable code, but I think it's useful to give people a clear way how to use GSAP tween and timeline with both class and hooks approach. If you think there is something missing or not clear enough in this guide please let me know. https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/ I will create more guides for React + GSAP and class approach would definitely be one of them. Hey Peter, No problem mate!!! we are all here pushing in the same direction. Since you have a dedicated section for GSAP+React, it would be nice to have this kind of samples and as many case scenarios covered in order to refer users to your site. Mostly the issue we (Blake and I) have is that the Hooks API ends up being confusing and somehow filled with this hurdles you have to navigate. @ZachSaucier already said it better than I can: 2 hours ago, ZachSaucier said: What an unintuitive way of handling things... Components are easier and their API makes more sense. Also that's why the more I work with Vue, the more I prefer it as an approach to build apps, because in most cases you have only one way to do things and that is a great feature for a library that helps you build apps. Also Vue allows to separate the HTML from the JS and finally in version 3 the composition API is going to be a great addition. I'll check the tutorial on the weekend and get back to you. Happy Tweening!!! 2 Link to comment Share on other sites More sharing options...
Share Posted July 11, 2020 39 minutes ago, Rodrigo said: Components are easier and their API makes more sense. One of the arguments for hooks is that you don't have to use this. So instead of using this.someValue, you now have to use someValue.current. I don't see how that is an improvement. 🤷♂️ 43 minutes ago, Rodrigo said: Also Vue allows to separate the HTML from the JS and finally in version 3 the composition API is going to be a great addition So simple. <template> <button @click="inc">{{ count }}</button> </template> <script setup> import { ref } from 'vue' export const count = ref(0) export const inc = () => count.value++ </script> 3 Link to comment Share on other sites More sharing options...
Author Share Posted July 12, 2020 This thread has been really informative and makes me think we jumped ship from class components too soon. It was unusual behaviour like this which made us move from Angular to React in the first place. Thanks, all for providing a few different solutions to this! 1 Link to comment Share on other sites More sharing options...
Share Posted July 13, 2020 On 7/10/2020 at 10:45 PM, OSUblake said: Better approach. export default function App() { const tl = useRef(); useEffect(() => { tl.current = gsap.timeline({ repeat: -1 }) .to(testEl.current, { duration: 1, opacity: 0, onComplete: update }); return () => tl.current.kill(); // return function to kill on unmount }, []); } Took the words out of my fingers here, @OSUblake. This is, more or less, the approach we use. For "on-load" animations we often use useCallback instead of useEffect. 2 Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now