Heart Of The Problem In React Native Animations

A guide to implementing animations in React Native and the main problem with the animation in React Native

Heart Of The Problem In React Native Animations

The Problem

Before getting further into the world of animations in React Native, let's first jump into understanding the heart of the problem that lies in javascript in general and particularly in react native when we'll be working with our animations. In React Native architecture smooth and nice animations depend on the communication between the UI thread and Javascript thread. Since animations run on a separate UI thread they are however easier to manage and control when there are animations that are fired and forgotten, but when it comes to animations like gesture animations for example when a screen is touched or a button is clicked that responds with a gesture than we simply have to also react with the events running on the Javascript thread. The event handling mechanism in the Javascript thread is asynchronous and our UI thread is synchronous therefore UI thread never waits for the Javascript thread to finish and that is where the HEART OF PROBLEM in react native with animations lies. Consider a click of the button which response with some gesture animation the UI thread will ask the Javascript thread to provide the gesture and if the javascript thread is busy executing some HTTP request which was also called on the button click gesture it won't be able to respond back immediately and eventually we'll drop that frame on which our gesture animation lies upon and the user experience will not be smooth and nice.

Let's Look at the problem visually

import React, { useEffect } from "react";
import { Animated, PanResponder, StyleSheet, View } from "react-native";
import { StyleGuide } from "../components";

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
  circle: {
    backgroundColor: StyleGuide.palette.secondary,
    height: 100,
    width: 100,
    borderRadius: 50,
  },
});

const CircleGesture = () => {
  const circlePosition = new Animated.ValueXY();
  const panResponder = PanResponder.create({
    onMoveShouldSetPanResponderCapture: () => true,
    onPanResponderGrant: () => {
      position.setOffset({
        x: position.x._value,
        y: position.y._value,
      });
      position.setValue({ x: 0, y: 0 });
    },
    onPanResponderMove: Animated.event([
      null,
      { dx: position.x, dy: position.y },
    ]),
    onPanResponderRelease: () => {
      position.flattenOffset();
    },
  });
  return (
    <View style={styles.container}>
      <Animated.View
        style={[styles.circle, position.getLayout()]}
        {...panResponder.panHandlers}
      />
    </View>
  );
};

export default CircleGesture;

Looking at the code above we are applying vanilla animations to the default animations library by javascript to visualize our communication problem between the UI and Javascript thread. We have a circle gesture that can be dragged around the screen

Screen Recording 2021-09-24 at 9.35.52 PM (2).gif Something like this. Now looking at the code we first initiate the instance of our animations coordinates new Animated.ValueXY() which is X and Y coordinates and the assigns those X and Y coordinates some default values using the PanResponder.create method that sets the value for our X and Y coordinates. to 0 initially using position.setValue({ x: 0, y: 0 }); on the view area of the screen. As you can see there are 2 more methods onPanResponderMove and onPanResponderRelease both of these functions are updating the position of the circle on the screen and update their positions, as we can see there is an event being triggered inside both the functions which mean Javascript thread would come into action to what's defined inside those events in our case there is simply update of new positions on the screen view using { dx: position.x, dy: position.y }, where dx and dy are the new coordinates of the circle location changed when it is clicked and dragged around the screen view. Till now the communication between our Javascript and UI thread are working smooth and the circle gesture would work as expected because our all our Javascript thread is doing is providing new coordinates to the UI thread so it's should be fast and smooth. But what if along with providing the new coordinates our Javascript thread has to loop through an array of 5000 items or call an external HTTP request and wait for it to resolve. Let's add something to our code which will keep our Javascript thread busy.

useEffect(() =>
    new Array(5000).fill(0).map(() => console.log("JS thread busy!"))
  )

Upon adding useEffect on every rerender our Javascript thread does not have to only just respond to the event of updating the new coordinates but also loop through an array of 5000 items.

Screen Recording 2021-09-24 at 9.35.52 PM (3).jpg

Now our circle gesture does not respond or moves as the Javascript is busy looping the array and cannot provide the new coordinates to the UI thread.

The Reanimation 2 library:

While the above problem can be solved in many ways using custom techniques we'll be using the Reanimation library the most used animations library for creating React Native animations which solves the problem of communication between threads uniquely. Reanimation solves the problem by working proactively on the UI thread and making it independent on its own to manage tasks related to animations. Well, how it does that? Reanimated uses simple functions to run completely on the UI threads and handles events updates. The only additional thing these functions have is worklet keyword and using the runOnUI() function to wrap the worklet function which allows it to run on the UI thread. I can go

function someWorklet(){
'worklet'
console.log("Function running on the UI thread")
}
function onPress(){
 runOnUI(someWorklet)('Howdy')
}

I can go on further explaining the Worklet function used in this library but it would be useless as there is already an amazing blog written for the use of Worklets inside Reanimation go check that out https://blog.swmansion.com/introducing-reanimated-2-752b913af8b3

There is a lot of amazing stuff and a lot of extra workloads which Reanimation allows us to wave off and help resolve the problem of communication between threads in Javascript.

What's next? I hope I have covered the main problems with the animations in React native but It would be a disservice if I won't mention the best youtube video to learn more about this awesome library also it's the one I took inspiration from from from while writing this Blog. https://www.youtube.com/watch?v=wEVjaXK4sYQ&t=562s .

If you enjoyed reading my blog and if you are not part of my journey where my mission is to educate and keep all my companions updated and covered about the most amazing and latest tech stack information available. Then do support me by liking, sharing, and commenting on my blog.