Conductor: Orchestrate Animation in React Native

Moses Gunesch
5 min readMar 28, 2017

React Native’s Animated API provides a great way for developers to create performant animations for their iOS and Android apps. It unpacks some of the moving parts typically hidden inside an animation engine, such as raw Animated values that can be tweened directly or interpolated into new values, an extremely versatile and powerful feature. Since it’s a React world, imperative tweens don’t target elements directly, but rather operate on declarative style props like transform.

At Instrument we’ve both loved Animated and found it cumbersome at times, mostly due to to the very way it unpacks so much code into our components. One viable fix is to use a JSX-based animation tool, which invariably will use Animated under the hood, but can offer a cleaner tag-based syntax for declaring fades and drifts.

Problem solved?

Welcome to the Tangle

Almost — it turns out that the high degree of manual control Animated provides is great for complex UI scenarios (and lord knows you’ll run into a few of those building apps)—so we do find ourselves circling back to it.

As a way to show Conductor in action I built an Airbnb-like pull-down menu, which is included as an example project.

The effect is pleasant and seems simple at first glance, but like a lot of things, it ain’t! The menu has 3 snap points (normal, expanded, and fully collapsed), and the screen is composed in 3 nested levels (content scroller, header, and the expanded-state menu). Although there’s just a single user input—scroll—there are no fewer than 12 interpolations. With this many moving parts, it’s a challenge to craft code that reflects the simplicity of what you see on the surface.

A similar story emerged when I built a functional design prototype of an in-run screen for a sports company’s running app. It was sort of like an MP3 player UI, with loads of readouts and controls that make things do stuff. 🤔The screen also had intro & outro sequences, which added to the number of tricks each component needed to do. The right way to build React is to break a big view into bite-sized chunks, but this can end up scattering parts of animations around. It’s hard to build a coherent set of visual effects when little bits of them end up buried several layers down in the component tree without any way to keep track of the larger context.

React’s flexible architecture simply didn’t scale well in these cases. The code grew into a tangle of component logic, state and animation, while coordinating the many moving parts became increasingly difficult.

All Rise for the Conductor

My solution to this snare, Conductor, is a new way to organize your work. The idea is simple: move all of the animation code for any substantial component into an empty wrapper.

A Conductor is just a standard React class that you create, name and write yourself. This gives you plenty of room for your animation work.

In its render method you decorate your main component with a <Conductor_> tag, passing all of your animated styles in an object:

And in your components you decorate your animated views with this tag:

This pipes the associated styles onto each view, no matter how deeply nested in the hierarchy that view’s component might be. And voila—components are free of Animated code, legible once more.

The trailing underscores in the component names mark that they’re not views, but that they just ‘decorate’ another component, adding functionality.

Trying this system out, it feels fun.🌤 First, it brings the ease of using a JSX-based library to Animated—to migrate an animation between components, just cut and paste.

Next, it was gratifying to reunite long-lost chunks of animation into a coherent script. I could ditch all the extra component states trying to keep track of what was going on and return to simple, readable sequences.

Another nice aspect was being free to write plain animation code without needing to think about where the target views lived, or their implementation details. Once tightly intertwined with a component’s view hierarchy and interaction handlers, Animated code is now promoted to its own corner office where it can manage a broad team of views with a light touch.

But for me the best part is how it eases the cognitive load. Rummaging through a bunch of files trying to track down some irksome timing or easing detail can really spoil a morning. Now, I know where to look!

#Experimental

There are a few potential drawbacks of the Conductor system, such as the fact that you no longer see what styles are declared on an animated node (just that it receives styles), the fact that components are tied to an outside class (and thus might be less portable), and that a layer of indirection is introduced which increases the need for inter-component communication.

I haven’t used Conductor enough to fully gauge these, but in my experience so far they don’t feel like deal-breakers.

  • Not seeing animated styles declared doesn’t bother me because the decorator tag clearly annotates that the view is being ‘puppeteered’. In practice this leaves absolute flexibility to change what’s animated at the Conductor level, and sort of offloads that responsibility. In general I was able to set up my views once, then walk away and focus on animation.
  • Coupling to an outside class seemed a bit sketchy going in, but also felt fine in practice. Components in these situations tend to be purpose-built, and that’s where the AnimatedNode_ tags end up—smaller atomic components just get imported and animated without modification. It’s also possible to nest Conductors. So to package a subcomponent with its own Conductor would be easy—just move some animation code.
  • The need to talk between components did add a bit of hassle, in that children need to send an event or callback up the chain to start an animation sequence, and it’s not as easy to notify the child when an animation completes. Using an event emitter is one solution, and as an extra Conductor provides a hook to fire callbacks.

This is definitely an experiment and I welcome your feedback, either here or at the repo’s Issues page. Thanks for trying it out!

https://github.com/mosesoak/react-native-conductor

--

--

Moses Gunesch

Just another software engineering dad in Portland, Oregon