# Usage with React

# In this tutorial your will learn how to use MoJS in React using React's Hooks API.

# Author: Jonas Sandstedt (opens new window)

Fork example on CodeSandbox (opens new window)

TLDR;

Assign your animation to an useRef immutable object inside a useEffect hook. Then you can use this ref to control the animation, like this: myRef.current.play()


# Install packages

Start by installing React, ReactDOM and @mojs/core from your favorite package manager. Here we use npm:

npm i react react-dom @mojs/core

# Create the animation component

Here is the final component that we're gonna build:

import React, { useRef, useEffect, useState, useCallback } from "react";
import mojs from "@mojs/core";

/**
 * Usage:
 * import MojsExample from './MojsExample';
 *
 * <MojsExample duration={1000}/>
 */

const MojsExample = ({ duration }) => {
  const animDom = useRef();
  const bouncyCircle = useRef();
  const [isOpen, setIsOpen] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);

  useEffect(() => {
    // Prevent multiple instansiations on hot reloads
    if (bouncyCircle.current) return;

    // Assign a Shape animation to a ref
    bouncyCircle.current = new mojs.Shape({
      parent: animDom.current,
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: duration,
      isShowStart: true,
      easing: "elastic.inout",
      onStart() {
        setIsAnimating(true);
      },
      onComplete() {
        setIsAnimating(false);
      },
    });
  });

  // Update the animation values when the prop changes
  useEffect(() => {
    if (!bouncyCircle.current) return;
    bouncyCircle.current.tune({ duration: duration });
    isOpen
      ? bouncyCircle.current.replayBackward()
      : bouncyCircle.current.replay();
    setIsOpen(!isOpen);
  }, [duration]);

  const clickHandler = useCallback(() => {
    // If the "modal" is open, play the animation backwards, else play it forwards
    isOpen ? bouncyCircle.current.playBackward() : bouncyCircle.current.play();
    setIsOpen(!isOpen);
  }, [isOpen]);

  return (
    <div ref={animDom} className="MojsExample">
      <div className="content">
        <h1>MoJS React Example</h1>
        <p>Using hooks</p>
        <button className="button" onClick={clickHandler}>
          {isAnimating && isOpen ? "Animating" : isOpen ? "Close" : "Open"}
        </button>
      </div>
    </div>
  );
};

export default MojsExample;

# Let's break it down!

First we create a useRef hooks, and takes advantage of its immutable object and assign our MoJS Shape animation to it. This way we can later use it to control our animation:


 


 












const MojsExample = () => {
  const bouncyCircle = useRef();

  useEffect(() => {
    bouncyCircle.current = new mojs.Shape({
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: 1000,
      isShowStart: true,
      easing: "elastic.inout",
    });
  });

  return;
};

By checking if the ref already has been assigned, we can prevent the animation from being instantiated again on re-renders (very useful when using hot reloads):





 














const MojsExample = () => {
  const bouncyCircle = useRef();

  useEffect(() => {
    if (bouncyCircle.current) return;

    bouncyCircle.current = new mojs.Shape({
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: 1000,
      isShowStart: true,
      easing: "elastic.inout",
    });
  });

  return;
};

To get the reference to DOM element we want to append our animation to, we can use a useRef hook, and attach it to a container:



 





 











 




const MojsExample = () => {
  const bouncyCircle = useRef();
  const animDom = useRef();

  useEffect(() => {
    if (bouncyCircle.current) return;

    bouncyCircle.current = new mojs.Shape({
      parent: animDom.current,
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: 1000,
      isShowStart: true,
      easing: "elastic.inout",
    });

    bouncyCircle.current.play();
  });

  return <div ref={animDom} className="MojsExample" />;
};

export default MojsExample;

Now lets add a button to play the animation when we click it. To control the animation, we can now reference the MoJS animation using bouncyCircle.current.play();



















 
 
 



 
 
 






const MojsExample = () => {
  const bouncyCircle = useRef();
  const animDom = useRef();

  useEffect(() => {
    if (bouncyCircle.current) return;

    bouncyCircle.current = new mojs.Shape({
      parent: animDom.current,
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: 1000,
      isShowStart: true,
      easing: "elastic.inout",
    });
  });

  const clickHandler = useCallback(() => {
    bouncyCircle.current.play();
  });

  return (
    <div ref={animDom} className="MojsExample">
      <button className="button" onClick={clickHandler}>
        Play animation
      </button>
    </div>
  );
};

export default MojsExample;

TIP

If we instead would want to play our animation directly, we could add bouncyCircle.current.play(); directly after the bouncyCircle declaration.

By passing props to our MojsExample function, we can control the initial values of the animation, and also .tune() the animation when they change.







 











 





 
 
 
 
 














/**
 * Usage:
 * import MojsExample from './MojsExample';
 *
 * <MojsExample duration={1000}/>
 */
const MojsExample = ({ duration }) => {
  const animDom = useRef();
  const bouncyCircle = useRef();

  useEffect(() => {
    if (bouncyCircle.current) return;

    bouncyCircle.current = new mojs.Shape({
      parent: animDom.current,
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: duration,
      isShowStart: true,
      easing: "elastic.inout",
    });
  });

  useEffect(() => {
    if (!bouncyCircle.current) return;
    bouncyCircle.current.tune({ duration: duration });
    bouncyCircle.current.replay();
  }, [duration]);

  const clickHandler = useCallback(() => {
    bouncyCircle.current.play();
  });

  return (
    <div ref={animDom} className="MojsExample">
      <button className="button" onClick={clickHandler}>
        Play animation
      </button>
    </div>
  );
};

As a final touch, lets add some methods to listen animation events, and use Reacts useState to save it as a local state:




 
 












 
 
 
 
 
 






 
 
 
 




 
 
 




 





const MojsExample = ({ duration }) => {
  const animDom = useRef();
  const bouncyCircle = useRef();
  const [isOpen, setIsOpen] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);

  useEffect(() => {
    if (bouncyCircle.current) return;

    bouncyCircle.current = new mojs.Shape({
      parent: animDom.current,
      shape: "circle",
      fill: { "#FC46AD": "#F64040" },
      radius: { 50: 200 },
      duration: duration,
      isShowStart: true,
      easing: "elastic.inout",
      onStart() {
        setIsAnimating(true);
      },
      onComplete() {
        setIsAnimating(false);
      },
    });
  });

  useEffect(() => {
    if (!bouncyCircle.current) return;
    bouncyCircle.current.tune({ duration: duration });
    isOpen
      ? bouncyCircle.current.replayBackward()
      : bouncyCircle.current.replay();
    setIsOpen(!isOpen);
  }, [duration]);

  const clickHandler = useCallback(() => {
    // If the "modal" is open, play the animation backwards, else play it forwards
    isOpen ? bouncyCircle.current.playBackward() : bouncyCircle.current.play();
    setIsOpen(!isOpen);
  }, [isOpen]);

  return (
    <div ref={animDom} className="MojsExample">
      <button className="button" onClick={clickHandler}>
        {isAnimating && isOpen ? "Animating" : isOpen ? "Close" : "Open"}
      </button>
    </div>
  );
};

# Create the root App and render the page

Finally we import our component and add it to the root of our site.

import React from "react";
import ReactDOM from "react-dom";

import MojsExample from "./MojsExample";

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <MojsExample duration={2000} />
  </React.StrictMode>,
  rootElement
);

You can see the full example and try it out here: CodeSandbox (opens new window). There is also a button example (opens new window) with a Burst animation using an object pooling array.

Usage with Server Side Rendering (SSR)

Note that this is a client-side library, and is not meant to be run on a server. So if you are using a library like Next.js, Gatsby, Nuxt.js or Angular Universal, make sure not to run your MoJS code on the server, just on the client side. How to do that differs from the library you are using. In React based libraries you can use the useEffect hook or a dynamic import (read more here (opens new window)).

Happy animating!