TLDW logo

Christopher Chedeau - Animated

By ReactRally

Summary

## Key takeaways - **React defaults to full subtree rerenders**: The way React works by default is that you're also going to rerender the entire subtree. This may sound like a crazy default but in practice most of your use cases do not need high performance and this is a really good default because it doesn't add many constraints. [03:49], [04:05] - **shouldComponentUpdate breaks isolation**: shouldComponentUpdate is a very good technique but it's impractical because if you want to animate the top component you need to refactor children owned by someone else, implementing immutable data structures and not using bound functions, breaking React's isolation. [05:33], [06:20] - **Static Container prevents child rerenders**: Wrap expensive child in a static container with a shouldUpdate prop that says I'm not animating so children don't rerender during animation to avoid dropping frames. In shouldComponentUpdate just return the shouldUpdate prop. [07:05], [07:36] - **Element caching skips identical diffs**: React has an optimization that if two elements are exactly the same (triple equal), it bails and doesn't rerender. Cache the element in an instance variable to control when the child gets rerendered. [08:12], [08:40] - **Animated uses data binding for perf**: For animations create an AnimatedDiv that registers change listeners on animated values and does direct DOM mutations on updates, using React lifecycle to remove listeners and avoid leaks and race conditions. React doesn't know about bindings so mangle props to replace them with real values at render time. [17:53], [21:02]

Topics Covered

  • React's Rerender Default Optimizes Most Cases
  • ShouldComponentUpdate Breaks Component Isolation
  • Data Binding Excels for Animations
  • Animated Values Enable Safe DOM Mutations
  • React's Versatility Powers Custom Strategies

Full Transcript

Hello. Hi everyone. My name is Christo and I'm also known as Visure on Internet and today I'm super excited to be here like at the third React conference and like if two years ago you told me that

there would be like a React conference I would I would I would not have believed you. So having one sublexity with all of

you. So having one sublexity with all of you like this is like blowing my mind.

But anyway, so I'm working on React Native and the goal of this project is to uh implement high quality mobile apps that are indistinguishable from native

apps. And we're using React to build

apps. And we're using React to build this. And one of the main changes that

this. And one of the main changes that we faced is that most of the mobile apps, they're mainly like gesture driven with animations. And what we found out

with animations. And what we found out is like React has not been designed for this and like very few people actually wrote animation using React. And so

today I'm going to explain you how we went on like trying to implement this.

So before we do that I'm going to show you like a few examples of animation we've been able to make. So the first one is the F8 app. So this is a

conference app like this one and you can see like the uh header on the left like they push each other like there's a smooth animation when you open like there's a scroll view like you can

animate while while dismissing and so all of this is written in JavaScript and here if you can see like uh the curve is actually animating

and there's a smooth like popover that's coming in like has animation when you transition in out.

And so this is like the first thing.

Then one of the project I worked on is an image gallery on the web and I couldn't make it work. And so this was like one of the big test of React Native. And we actually managed to

Native. And we actually managed to implement like a really smooth uh image gallery using React Native. And so you can see like you got like transition rotation like it smoothly moves like

this.

And finally uh some kind of animation that are harder to implement on the web like oh I want to animate everything like text animating like everything's coming text

is coming in and so those are the kind of animation that you can implement using react native and if you and those are the kind

of animation that you see in like the Facebook apps or like normal native apps and so today I'm not going to talk to you about how to implement those animations, but instead I'm going to

explain to you how we got those animations to be performance. And so for this, I'm going to explain to you five techniques that we used. So the last one

is going to be the one we actually ended up using and it's a bit of a secret technique.

[Music] So I'm going to focus on one only one example which is just an element sliding in uh your view your viewport. So the

way you implement this using react like if you follow the tutorial you're first going to set your progress of the animation using uh in the state then

you're going to use inline styles in order to like update the left style and then on request animation frame you're going to set state the left uh instance

variable and uh to update and so this is going to work like you're going to have an animation But now let's look at the

performance characteristic of this. So

you're going to set state on this node and it's going to rerender this node.

But the way react works by default is that you're also going to rerender the entire sub tree. And so this may sound like a crazy default because this is not

opt like this sounds like not optimized but what we found out is in practice like this is like most of your use cases like they do not need like high

performance and this is a really good default because it doesn't add many constraints to you. And so what we're going to see is in order to uh to

improve the performance, we're going to add constraints to the developer. And so

we want to only add constraints where you're on the critical path or you are uh and you have like some performance issues.

So now let's assume that uh it takes more than than 16 milliseconds to rerender this entire tree. So if you keep following the React documentation,

uh the suggestion that we give you is to implement should component update. So

it's a hook that given the previous props in state and the next prop and state, you can tell React like okay React I'm confident that nothing is going to change like just keep

rerendering this part.

And so this is actually a very good technique and uh we've like most of the performance issues I've seen people have with React in the past three years have

been fixed using should component update but it's not uh like it's a bit impractical to implement in practice because you're

not working alone on your project and usually the way you split up your project is that each component is owned by someone. So if you want to animate

by someone. So if you want to animate the top component, you basically own it.

And so all of the children, they're owned by someone else. And now if you want to implement shoot component updates on an element, you you need to refactor it a bit. So you need to

implement use immutable data structures, not use bound function and like something some things like this. And

while like most of the time this is not a big refactor, this is still like modifying this code. And so now we break

one of the like big uh characteristic of React which is isolation.

When you want to animate the top component, you should not have to modify the other components. And so this breaks

this assumptions. And so if you had a

this assumptions. And so if you had a magic wand to fix this problem, what you would likely do is want to move the shoot component update uh decision up in

the parents.

And so if you do that, then if you animate the top component, then you're now responsible to make sure uh the

children are not being rerenderled.

And so the way to implement there's actually a way to implement this using React uh in a module with a component called that we call static container.

And so the way uh you use it is you wrap your expensive child in a static container with a prop called should update that says oh I'm not animating.

So if I'm animating I don't want any child to rerender because I don't want to drop a frame. And so the way to implement this is fairly trivial. You

just create a react component. You just

forward all the children that that have been passed. And then in shoot component

been passed. And then in shoot component update, you just give the should update prop. And so using this technique,

prop. And so using this technique, you're able to control when the component like the ch the child component gets rerendered. And so this is one way to do it. But there's

actually another way to achieve the same effect using React. And this is called element caching. So in order to

element caching. So in order to understand what this technique is, we need to understand what is an element.

So a react element is anything returned by JSX by a JSX tag. And you can and if you're not using JSX, you can use React.createelement to return it. And so

React.createelement to return it. And so

React has an optimization inside that says that when it's going to diff two elements, if it's exactly the same element like triple equal the same element, there's not is going to bail.

It's going to say like, okay, this element didn't change. I'm going to not rerender it. And so the way to use this

rerender it. And so the way to use this technique is by caching the uh the element in the instance variable like

this. And then you're able to like

this. And then you're able to like control when this child gets rerendered.

And so those two techniques are very useful uh to improve the performance of your app, but they are not silver bullets. And

bullets. And one of the big issue that they have in common is that they add

uh a new bug that disappeared from React which is uh race conditions. So

one of one of the uh one of the bug that can happen in this case is let's say there's an update that comes in while you're animating. This means that the

you're animating. This means that the animation is going to say like no I don't want to rerender this child element and then the animation finishes

and if you don't rerender then you're going to be you're going to have dropped this update and then you're going to like have some inconsistent state and if

some like comp and if this component like rerenders sometime in the future then your new like your updated version is going to pop in from nowhere and it's going to be a bad experience for the user.

And so now if you look at uh this technique what we are trying to do is to like say react like I don't want you to control

the def algorithm like I know better than you. I want to do my own thing. And

than you. I want to do my own thing. And

so when you think about it the only thing that we want to change here is the uh left attribute. And so we don't want to rerender the entire like component

and the children like we just want to set this one value. And so there's actually a way for to tell React like to do that which is by implementing row d

mutations. So we're going to get outside

mutations. So we're going to get outside of react diffing algorithm on world and we're going to say like react I know what I'm doing. Let give me the real

dumb node I'm going to use like style.

equal to 10 px.

And so the way you implement this is actually pretty trivial. Uh on your own update, instead of doing set state, you're going to do react.findom node

this style. equal to value plus pixel because of CSS.

And so this is going to be like the most performance thing you can do like you're doing like road d mutation and like you get all the performance that you want.

But unfortunately the same way as the other technique it has some big downsides. And the big one with this technique is

that you're going to have to deal with undefined values because if the component gets unmounted from the DOM, then you like the DOM element doesn't exist

anymore. And so now you're going to uh

anymore. And so now you're going to uh try to set the style on an element that doesn't exist anymore. It's going to throw an exception. And we've got like

so many of those in React Native like this is like this is not ideal. And also

like we're still not uh uh we still have many race condition that can happen because on one side there's a normal react rendering that

has a value for the left but you also inject on every animation frame a value uh for the left. And now if any of those

two are not uh do not want to update the value to the same value then you're going to have a race condition on guess in post and so

this is like those techniques are very useful but they have like some like big trade-offs and one thing that's I really hate about Zoo is the the reason why I

went into React in the first place is because It eliminated all of those race condition and like state management bugs that you had in like previous application. And now if

you want to have high performance react application like you need to go back to all of those like bugs and like fight with them again which is not an ideal

solution. So I wanted to find to see if

solution. So I wanted to find to see if we could find a way to get the best of both worlds. the

both worlds. the rodom mutation like uh performance and also like all the safety benefits of react and so I went back to the first

example and looked at it and star at it and I was like okay so actually what I'm saying what I'm giving to react is

I say that the div style and left equals to this state.

So why is React not able to whenever I update this left just update

this attribute? Why does it have to do

this attribute? Why does it have to do an entire diff algorithm and regenerate a lot of things? So what I would really like to do is for React to remember that

uh this setleft is equal to divsy.left

left and whenever I set states I'm going to like output a road mutation and so before we go to the next slide I want to take a moment and to forget

everything you know about react and I want you to keep an open mind because this technique actually has a name it's called data by name

and so you may be wondering like Why am I here today and talking to you about React about data binding when Pete Hunt like a year ago went on stage and said

like the reason why we use React is because data binding sucks basically.

And so before like in order to uh to answer this question we need to figure out like what what are the performance consideration of data binding. So in

order to do data binding for every binding that you have in your app, you're going to have to allocate some object. So it's going to take some

object. So it's going to take some memory and you're going to like have to allocate this object. So you're going to like initialize it and take some CPU to do it, but

you're going to get really high performance updates. You're going to get

performance updates. You're going to get all of one updates. And so what Pit was talking about is using data binding for

user interface like for the bulk of your UI. And so this is actually not a good

UI. And so this is actually not a good fit because in a typical UI your model is going to be much bigger than your view. Like you're going to pre-cache uh

view. Like you're going to pre-cache uh some other pages. You're going to like you're going to have a lot of things that you're going to not display. And

because you're going to have a data binding element like a binding element for every single uh model element that you have is going to cost a lot of

memory.

And also data binding is optimized when you just have a like a simple mutation on a single attribute or value or something like this. But this is not

the usual workload for most apps. for

most apps like the app is static or maybe like the on Facebook like the like count is going to change but most of it is static and then the next time you change something you're just going to

navigate somewhere else and like tear down the entire view and create a new one and so with this workload like startup time

matters a lot like when the user goes to facebook.com like you want something to be displayed on the screen like as fast as possible and when you click somewhere you want to change as fast as possible and you don't have the time to set up

all of those bindings.

And so for user interfaces, data binding is actually not a good fit. And the

React model is actually much more efficient for this. But then when you look at animations, you have only a few attribute that you want uh to want to animate. You only

animate like a few elements. And when

you animate, you only animate like the left, the top position, maybe a scale or opacity, but that's it. And the

trade-off that we want is we want the absolutely best performance for every single frame. Like we cannot go past 16 milliseconds be because we

left the train like the train goes and like we lost a frame. And so

it's much more important to start time.

But even in this case like startup time to allocate like a dozens of bindings like this is like pretty cheap and so data mining for animation is actually a

very good solution. So now let's see how to actually implement data mining uh using react.

So the first thing that we want to do is to not uh use a normal div because react doesn't know uh about data mining and if

you use a normal div like you won't have data mining. So what we want to do is to

data mining. So what we want to do is to create a new component that looks and feels like a div but has this data binding ability. And so in the animated

binding ability. And so in the animated uh library that we use in React Native, we just call it animated div.

And the second thing that we need to change is we cannot use an initial state which is uh a state which is just a number because in JavaScript you cannot

tell when an uh just a number changes.

What you need is to wrap this number into a binding and so in animated it's called a animated value and then when you have this binding then you can set

the value on this binding and it's going to propagate somehow to the do. So let's

see how to implement this propagation.

So the first thing we want to do is to create a div element that just looks like a div. And so for this you can just

create a a component that renders a div and just forwards all the props.

And so now let's see how to actually implement the binding.

So on component will receive props we're going to look at the left style which is a binding and we're going to register a

change listener.

And whenever this value changes, we want to find the dumb node and do a row dumb mutation. So we get this very fast

mutation. So we get this very fast update. But if you implement it like

update. But if you implement it like this, then you're going to leak memory because every time you're going to rerender, you're going to allocate new

binding that are doing exactly the same thing. And if the component unmounts,

thing. And if the component unmounts, like the bindings are still going to be there and you're like it's still going to be uh not ideal.

So, React is actually very good because it provides you all of those life cycle methods that tells you like when a

component has been updated, when it unmounts. And so, using Zoo, uh, we can

unmounts. And so, using Zoo, uh, we can remove the previous listeners before we receive the next props and before we unmount. And so thank to this

we're able to solve all of the race conditions, all of the memory links. Like we don't need to care

memory links. Like we don't need to care about it. Like anytime the component

about it. Like anytime the component like is unmounted, then the bindings is going to know that it has one less

element to be animating. And when it goes down to zero, we can even stop the current animation. And so using this it

current animation. And so using this it gets it gets us like all the reliability and of react.

And so this almost work but we need to deal we need to do like one more thing which is that if you just if you just

pass uh animating uh binding to react it doesn't know what to do with like I don't know what to do with an object uh as a style attribute. So what we need to

do is to implement is to like mongle the props and find all of the bindings and just replace them

with a real value and store it somewhere. And in render we're going to

somewhere. And in render we're going to render those so that the div element of React actually knows how to work with them.

And so this is just like a dummy example of how to do it. And as you can see, like we're actually like doing some work like we are like allocating new objects.

We are doing some bindings, but you're only going to do this work like at startup time and then you're going to get very very fast updates.

So I've explained to you like five of those techniques and those techniques are not limited to animations. like in your app

you may have performance bottleneck and I highly encourage you to uh use one of those and one of the great thing about React is

that it provides this diff algorithm by default and this is very good for the most of your use cases but if you want to use if you want to implement a

different uh update strategy like rom mutation data binding or like static container or whatever you are able to do that and I think this is one of the best

part of React and why I'm so excited about the project is because it gives you the versatility to do all of this and all of those updates are actually scoped

into a component. So you write all of this crazy stuff in this component and then from the outside world of React like it's just behaves like a normal

component and uh you have no idea if it's like if it's simp using the normal uh rendering strategy or some crazy stuff and

I'm so thankful for React for being as versatile. So thank you very much.

versatile. So thank you very much.

[Applause]

Loading...

Loading video analysis...