Jump to content
Search Community

GSAP & React error: setTimeout is not supported in server-rendered Javascript

Marina Gallardo test
Moderator Tag

Recommended Posts

Hi community ,

 

this is my first project with React and GSAP. I really love the work we have created, but we have a big issue: 
 

In the project, we are using server side render with reactjs.NET. When we try to render a component with the gsap library we got this error:

 

setTimeout is not supported in server-rendered Javascript


Here is an image with the error too.

 

please, help me with some ideas, I made great things with gsap and I dont want to dont  finally use the library and I have to finish the project next week... 

 

thank you very much 

AE4F306C-34E8-4CBA-AAAD-9C0A9F0F3DBE.png

Link to comment
Share on other sites

I am using yarn install for GSAP library, this is the component we are trying to make SSR and the animation setup:

import React, { useState, useEffect, useRef } from 'react';
import { PropTypes as pt } from 'prop-types';
import { useSliderSwipe, useObserver } from 'React/custom_hooks';
import { TimelineMax, TweenMax } from 'gsap/all';
 
import './style.scss';
 
import { TagTitle, InputRangeSlider, Link } from 'React/components';
 
const CifrasSlider = ({ title, frames, module_title, lead_text, module_description, module_cta }) => {
const [activeFrame, setActiveFrame] = useState(1);
 
const imagesContainerWidth = { width: `${100 * (frames.length + 1)}vw` };
const headerSliderComponent = React.createRef();
const cifrasRef = React.createRef();
 
if (frames.length > 1) {
useSliderSwipe(headerSliderComponent, setActiveFrame, frames.length);
}
 
const [observer, setElements, entries] = useObserver({
root: null,
threshold: 0.5,
});
 
const tl = new TimelineMax({ delay: 0, repeat: 0 });
 
useEffect(() => {
 
cifrasRef.current.firstElementChild.querySelector('.number-inner').classList.add('slow--y');
cifrasRef.current.firstElementChild.querySelector('.title--xs').classList.add('slow--y');
 
const elements = headerSliderComponent.current.querySelectorAll('.slow--y');
 
TweenMax.set(elements, { opacity: 0, y: 90 });
setElements(elements);
}, [setElements]);
 
useEffect(() => {
entries.forEach((entry) => {
// buscamos los elementos que son visibles y entonces aplicamos la animación.
if (entry.isIntersecting) {
let lazyItem = entry.target;
tl.to(lazyItem, 1, { opacity: 1, y: 0 });
observer.unobserve(lazyItem);
}
});
}, [entries, observer, TimelineMax]);
 
return (
<div className="module grid cifras-slider" data-active-frame={activeFrame} ref={headerSliderComponent}>
<TagTitle style="slow--y" title={title} />
{module_title && <h3 className="module_title title--l slow--y">{module_title}</h3>}
{lead_text && <h4 className="lead_text body--l slow--y">{lead_text}</h4>}
{module_description && (
<p className="module_description body--m">
{module_description}
{module_cta && (
<div className="module_cta">
<Link path={module_cta.path} type="arrow">
{module_cta.label}
</Link>
</div>
)}
</p>
)}
 
<div ref={cifrasRef} className="cifras-container" style={imagesContainerWidth}>
{frames.map((frame, idx) => {
const { value, unit, caption } = frame;
return (
<div className="data-point" key={`data-point-${idx}`}>
<h3 className="number cifras--xl">
<div className="number-container">
<div className="number-inner">
{value}
{unit && <small>{unit}</small>}
</div>
<p className="title--xs short-descritpion">{caption}</p>
</div>
</h3>
</div>
);
})}
</div>
 
{frames.length > 1 ? (
<InputRangeSlider
changeHandler={setActiveFrame}
framesCount={frames.length}
activeFrame={parseInt(activeFrame)}
description={module_description}
frameDescription={frames[activeFrame - 1].descr}
/>
) : (
<hr className="separator slow--y" />
)}
</div>
);
};
 
CifrasSlider.propTypes = {
title: pt.string,
frames: pt.arrayOf(
pt.shape({
value: pt.string.isRequired,
unit: pt.string,
caption: pt.string,
descr: pt.string,
})
).isRequired,
module_title: pt.string,
lead_text: pt.string,
module_description: pt.string,
module_cta: pt.shape({
path: pt.string.isRequired,
label: pt.string.isRequired,
}),
};
 
export default CifrasSlider;

 

 

Then we are rendering at server through this plugin:

https://reactjs.net/

 

Thank you for your support, I really don't know how to make it work or do not render that part and import the library once is rendered...  I also have webpack.config.

 

Link to comment
Share on other sites

Hi @Marina Gallardo,

 

Check with version of GSAP you're loading. The syntax you're using is all GSAP v2, and you're likely pulling in GSAP3.

Also, I find it best to set my timeline variable in useRef (to help avoid issues with there being no window, etc).

My setup generally looks something like this:

 

 

 

import React, { useEffect, useCallback, useRef } from 'react';
import { useInView } from 'react-intersection-observer';
import gsap from 'gsap';

const tl = useRef();

const animationElementRef = useCallback((node) => {
	const el = node.children;

	tl.current = new gsap.timeline({ paused: true });
  	tl.current
		.to(el, {autoAlpha: 1});
}

const [lazyRef, inView, element] = useInView({
    threshold: 0,
    rootMargin: '-250px 0px',
    triggerOnce: true, // this will unobserve
});

useEffect(() => {
    if (inView) {
      
      tl.current.play();
      
      // or you could very easily do something like this, which is more inline with your code: 
      const el = element.target;
    
      gsap.timeline()
        .to(el, { autoAlpha: 1, duration: 1 })
      	.to(el, { rotation: '360deg'});

		//or just gsap.to(el, {autoAlpha:1, duration: 1}) //no need for a timeline
    }
  
    // cant remember if this needs to go in a seperate useeffect or not
  	return () => {
      tl.current.kill();
    };
}, [inView, element]);


return (
  <CustomComponent ref={animationElementRef}>
	<LazyComponent ref={lazyRef} />
  </CustomComponent>
);

I use https://github.com/thebuilder/react-intersection-observer for my Intersection observer code, but the setup will be very similar with what you have.

 


Please forgive any errors in that code. Most of it is from memory, and is just to give you an idea for how to use GSAP with react and (react) Intersection Observer...

  • Like 3
Link to comment
Share on other sites

Make sure you have everything setup correctly... client side code separate from server side code.

https://reactjs.net/bundling/webpack.html

 

Or use gsap in a script tag, like from cdnjs.

 

@model IEnumerable<ReactDemo.Models.CommentModel>
@{
    Layout = null;
}
<html>
<head>
    <title>Hello React</title>
</head>
<body>
    @Html.React("CommentBox", new
    {
        initialData = Model,
        url = Url.Action("Comments"),
        submitUrl = Url.Action("AddComment"),
        pollInterval = 2000,
    })
    <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.development.js"></script>
    <script crossorigin src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.development.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/remarkable/1.7.1/remarkable.min.js"></script>


	<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.1.1/gsap.min.js"></script>

    <script src="@Url.Content("~/js/tutorial.jsx")"></script>
    @Html.ReactInitJavaScript()
</body>
</html>

 

 

  • Like 1
Link to comment
Share on other sites

3 hours ago, Marina Gallardo said:

If i use gsap as a script the way you say, can I run the animations the same way in react?

I'm no server side expert so I could be wrong here, but I believe that so long as the bundled JS comes after the import of GSAP via the script tag then the bundle shouldn't care that it's from a script tag.

Link to comment
Share on other sites

3 hours ago, Marina Gallardo said:

If i use gsap as a script the way you say, can I run the animations the same way in react? 

Yes, you just wouldn't need to @import gsap.

Also/again, looking at your code, what version of GSAP are you using?

You may try importing as such:
import gsap from 'gsap';

 

Instead of {timelinemax, tweenmax} from the gsap/all

 

 

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