Hot questions for Using Lottie in reactjs

Question:

Im trying to load a lottie-web animation in my react project but i keep getting this error and cant understand what it means

InvalidStateError: responseText is only available if responseType is '' or 'document'.

Here is my React component below:

import React, { Component } from "react";
import "./Submit.css";
import PropTypes from "prop-types";
import lottie from "lottie-web";

class Submit extends Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    lottie.loadAnimation({
      container: document.getElementById("animation"), // the dom element that will contain the animation
      renderer: "svg",
      loop: true,
      autoplay: true,
      path: "../anim/email.json" // the path to the animation json
    });
  }

  render() {
    return <div id="animation" />;
  }
}

Submit.propTypes = {};

export default Submit;

Any thoughts on what it could be?


Answer:

You can use animationData instead of path prop lottie.loadAnimation supports. The only thing you have to do is to convert the email.json file into a js file and save the json object inside of it into a const. If you export this const and then import it at the file you want to use it as :

 //email.js file
const email = {
//the json object
}

export default email.



//your other file

import Email from "../anim/email.js"

 lottie.loadAnimation({
      // the dom element that will contain the animation
      container: document.getElementById("animation"), 
      renderer: "svg",
      loop: true,
      autoplay: true,
      animationData: Email // the path to the animation json
    });

Question:

I've got a lottie animation that I trigger from a parent component, the problem is that for some reason the entire lottie component layer is clickable and initiates the animation.

This is the code code that I'm using:

constructor(props) {
    super(props);
    this.state = {
      isStopped: true,
      isPaused: false,
      Animated: 0,
    };
    this.defaultOptions = {
      loop: false,
      autoplay: false,
      animationData: animationData
    };
  }

  clickHandler = () => {
    this.setState({ 
        isStopped: false,
        Animated: 0
    });
    console.log("clicked");
  };

  render() {
    return (
      <div id="ethdrop">
        <Lottie
          className='animation-class'
          options={this.defaultOptions}
          isStopped={this.state.isStopped}
          isPaused={this.state.isPaused}
          Animated={this.state.Animated}  
        />
    </div>
    );
  }

This is how the dom looks like:

I can't find anything that might be triggering this if any of you have any idea please let me know.


Answer:

There is an undocumented property: isClickToPauseDisabled. When this is set to false (which is the default value), you can pause and resume your animation by clicking on it. If you don't want this to happen, set this property to true.

Example:

<Lottie options={this.defaultOptions}
    ...
    isClickToPauseDisabled={true} 
/>

Reference: https://github.com/chenqingspring/react-lottie/pull/54

Question:

I need your help. I'm trying to make a div show/hide inside the send button of the form using React's state when submitting the form using a promise inside Formik. I know it has something to do with binding 'this' but I can't figure out where to bind it. I want to do that so it shows a litter check mark when the submission has been successful.

I've tried setting:

const that = this;

And then using 'that' with the checkSuccesBoxShowHandler method. But it throws me this in the console:

TypeError: Cannot read property 'checkSuccesBoxShowHandler' of undefined

Here's the code:

class CForm extends React.Component {

    state = {
        checkSuccesBoxShow: false
    }

    checkSuccesBoxShowHandler = () => {
        this.setState({
            checkSuccesBoxShow: true
        });
        setTimeout(() => {
            this.setState({
                checkSuccesBoxShow: false
            })
        }, 3000);
    }

    render() {
        const {
            errors,
            touched,
            isSubmitting,
            handleSubmit,
            isValid,
            bgColor
        } = this.props;

        const checkSuccessLottieOptions = {
            loop: false,
            autoplay: true,
            animationData: checkSuccessLottie,
            name: 'checkMarkSuccess',
            rendererSettings: {
                preserveAspectRatio: 'xMidYMid slice'
            }
        };

        return (
            <Form className={classes.contactForm} onSubmit={handleSubmit}>
                <div className={classes.inputBox}>
                    <label className={touched.name && errors.name ? [classes.label, classes.labelError].join(' ') : classes.label} >Mi nombre es
                    <ErrorMessage name='name' component='p' className={classes.error} />
                        <Field type='text' name='name' placeholder='Tu nombre' autoComplete='name' className={bgColor ? [classes.inputElement, classes[bgColor]].join(' ') : classes.inputElement} />
                    </label>
                </div>
                <div className={classes.inputBox}>
                    <label className={touched.email && errors.email ? [classes.label, classes.labelError].join(' ') : classes.label} >Contáctame a
                    <ErrorMessage name='email' component='p' className={classes.error} />
                        <Field type='email' name='email' placeholder='tu@correo.com' autoComplete='email' className={bgColor ? [classes.inputElement, classes[bgColor]].join(' ') : classes.inputElement} />
                    </label>
                </div>
                <div className={classes.inputBox}>
                    <label className={touched.phone && errors.phone ? [classes.label, classes.labelError].join(' ') : classes.label} >Mi número de teléfono es
                    <ErrorMessage name='phone' component='p' className={classes.error} />
                        <Field type='phone' name='phone' placeholder='Tu número de teléfono' autoComplete='tel' className={bgColor ? [classes.inputElement, classes[bgColor]].join(' ') : classes.inputElement} />
                    </label>
                </div>
                <div className={classes.inputBox}>
                    <label className={touched.company && errors.company ? [classes.label, classes.labelError].join(' ') : classes.label} >Trabajo en
                    <ErrorMessage name='company' component='p' className={classes.error} />
                        <Field type='text' name='company' placeholder='Tu compañía' autoComplete='organization' className={bgColor ? [classes.inputElement, classes[bgColor]].join(' ') : classes.inputElement} />
                    </label>
                </div>
                <div className={classes.inputBox}>
                    <label className={touched.message && errors.message ? [classes.label, classes.labelError].join(' ') : classes.label} >Mensaje
                    <ErrorMessage name='message' component='p' className={classes.error} />
                        <Field component='textarea' name='message' placeholder='Empieza a escribir...' autoComplete='off' className={bgColor ? [classes.textAreaElement, classes[bgColor]].join(' ') : classes.textAreaElement} />
                    </label>
                </div>
                <button type='submit'
                    className={classes.sendBtn}
                    disabled={isSubmitting || !isValid}
                >
                    <span style={isSubmitting ? { display: 'none' } : null}>Enviar</span>
                    <div className={isSubmitting ? [classes['sk-circle'], classes.showing].join(' ') : classes['sk-circle']}>
                        <div className={[classes['sk-circle1'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle2'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle3'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle4'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle5'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle6'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle7'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle8'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle9'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle10'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle11'], classes['sk-child']].join(' ')}></div>
                        <div className={[classes['sk-circle12'], classes['sk-child']].join(' ')}></div>
                    </div>
                    <div style={this.state.checkSuccesBoxShow
                        ? { display: 'inline-block' }
                        : { display: 'none' }} >
                        <Lottie
                            options={checkSuccessLottieOptions}
                            height={50}
                            width={50}
                        />
                    </div>
                </button>
            </Form>
        );
    }
};

const ContactForm = withFormik({
    mapPropsToValues({ name, email, phone, company, message }) {
        return {
            name: name || '',
            email: email || '',
            phone: phone || '',
            company: company || '',
            message: message || ''
        }
    },
    validationSchema: Yup.object().shape({
        name: Yup.string().required('El nombre es requerido'),
        email: Yup.string().email('Email invalido').required('Email requerido'),
        phone: Yup.number().typeError('Debe ser un número telefónico'),
        company: Yup.string(),
        message: Yup.string().required('Mensaje requerido')
    }),
    handleSubmit(values, { resetForm, setSubmitting }) {
        // This is the endpoint we created in our API Gateway. This is where we make our POST request, which calls our Lambda function.
        const endpoint = '***********';

        // We replace line to keep format of text in message value
        const messageText = values.message;
        const formattedMessageText = messageText.replace(/\n/g, '<br />');
        let newValues = { ...values };
        newValues.message = formattedMessageText;

        // Here, we instantiate our Request. This is a special object used by the Fetch API so it knows where to send data, what data to send, and how to send it.
        var lambdaRequest = new Request(endpoint, {
            method: 'POST',
            // Quick note: 'no-cors' mode is for development on localhost only!
            mode: 'no-cors',
            body: JSON.stringify(newValues)
        });

        const that = this;

        // Call the Fetch API to make our request
        fetch(lambdaRequest)
            .then(response => {
                console.log(response)
                resetForm();
                setSubmitting(false);
                that.checkSuccesBoxShowHandler();
            })
            .catch(err => console.log(err));

    }
})(CForm)

export default ContactForm

Answer:

Because you are inside withFormik, which is a HOC (High Order Component), you can't access the component state, so you will need another approach.

You can use setStatus which is passed to your component by this.props.form.status.

So when you want to show the button, you call setStatus with some flag or value and in your component you do a conditional so show/hide it.

You can see this problem in this issue where this answer your problem.

For your case, you need to do something like

handleSubmit(values, { resetForm, setSubmitting, setStatus }) {
    // Call the Fetch API to make our request
    fetch(lambdaRequest)
        .then(response => {
            console.log(response)
            resetForm();
            setSubmitting(false);
            setStatus(true); // setStatus  to communicate with the component
            that.checkSuccesBoxShowHandler();
        })
        .catch(err => console.log(err));
}

And in your component

{this.props.form.status && <Lottie
    options={checkSuccessLottieOptions}
    height={50}
    width={50}
/> 
}

Question:

I use react-lotties and i want to put differents animations in many div changing only the url of lotties

I am a react beginner, be kind please :)

this is my script :

  • My lotties Component :
    import React, { Component } from "react"
    import Lottie from "react-lottie"
    import animationData from "./lotties1.json"
    import animationData from "./lotties2.json"
    import animationData from "./lotties3.json"
    import "./lotties.css"

    class LottiesC extends Component {
      render() {
        const defaultOptions = {
          loop: true,
          autoplay: true,
          animationData: animationData,
          rendererSettings: {
            preserveAspectRatio: "xMidYMid slice",
          },
        }

        return (
          <div className="x">
            <Lottie options={defaultOptions} height={600} width={600} />
          </div>
        )
      }
    }

    export default LottiesC

-My index.js Component:

       import React from "react"
        import Navigation from "../components/Navigation"
        import LottieControl from "../components/LottiesC"

        const index = () => {
          return (
            <section className="index">
              <div><LottiesC animationData ={lotties 1}  /> </div>
               <div><LottiesC  animationData ={lotties 2} /> </div>
              <div><LottiesC animationData ={lotties 3}  /> </div>
            </section>
          )
        }

        export default index


Answer:

As already mentioned by @Dellirium you should be passing different values over props to your components.

Here is how it works:

const animationDatas = [
  { id: 'first' },
  { id: 'second' },
  { id: 'third' },
];

class LottiesC extends React.Component {
  render() {
    const defaultOptions = {
      // loop: true,
      // autoplay: true,
      animationData: this.props.animationData,
      // rendererSettings: {
      //  preserveAspectRatio: "xMidYMid slice",
      // },
    }

    return (
      <div className="x">
        {/** <Lottie options={defaultOptions} height={600} width={600} /> **/}
        {JSON.stringify(defaultOptions)}
      </div>
    )
  }
}

const App = () => {
  return (
    <section className="index">
      <div><LottiesC animationData={animationDatas[0]}  /></div>
       <div><LottiesC  animationData={animationDatas[1]} /></div>
      <div><LottiesC animationData={animationDatas[2]} /></div>
    </section>
  )
}

ReactDOM.render(<App />, document.querySelector('#app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

<div id="app"></div>

Question:

container is complaining because HTMLDivElement | null is not assignable to Element type

export const Loader: React.FC = () => {
  const element = useRef<HTMLDivElement>(null);  
  useLayoutEffect(() => {
    lottie.loadAnimation({
      animationData,
      container: element.current,
      loop: true
    });
  });

  return (
    <Wrapper>
      <div ref={element}></div>
    </Wrapper>
  );
};```

Answer:

You have to make sure element.current has a value before you call loadAnimation. Try this:

export const Loader: React.FC = () => {
  const element = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if(element.current) // add this
       lottie.loadAnimation({
         animationData,
         container: element.current,
         loop: true
       });
  }, [element]); // add this, it triggers this effect everytime element changes

  return (
    <Wrapper>
      <div ref={element}></div>
    </Wrapper>
  );
};

OR

container: element.current as HTMLDivElement,    

OR

container: element.current!,    

The first option is the cleanest, since it's less error prone.