Animating With React, Redux, and d3
Check out Swizec's article on how to create a particle generator using React, Redux, and d3.
Join the DZone community and get the full member experience.
Join For Freethis is a particle generator . it makes tiny circles fly out of where you click. hold down your mouse and move around. the particles keep flying out of your cursor.
on mobile and only have a finger? that works, too.
i’m a nerd, so this is what i consider fun. your mileage may vary. please do click in the embed and look at those circles fly. ain’t it cool?
here’s how it works
the whole thing is built with react, redux, and d3. no tricks for animation; just a bit of cleverness.
here’s the general approach...
we use react to render everything : the page, the svg element, the particles inside. all of it is built with react components that take some props and return some dom. this lets us tap into react’s algorithms that decide which nodes to update and when to garbage collect old nodes.
then we use some
d3 calculations and event detection
. d3 has great random generators, so we take advantage of that. d3’s mouse and touch event handlers calculate coordinates relative to our svg. we need those, and react can’t do it. react’s click handlers are based on dom nodes, which don’t correspond to
(x, y)
coordinates. d3 looks at real cursor position on screen.
all particle coordinates are in a redux store . each particle also has a movement vector. the store holds some useful flags and general parameters, too. this lets us treat animation as data transformations. i’ll show you what i mean in a bit.
we use actions to communicate user events like creating particles, starting the animation, changing mouse position, and so on. on each requestanimationframe, we dispatch an "advance animation" action .
on each action, the reducer calculates a new state for the whole app. this includes new particle positions for each step of the animation.
when the store updates, react flushes changes via props and because coordinates are state, the particles move .
the result is smooth animation.
keep reading to learn the details. the code is also on github .
a version of this article will be featured as a chapter in my upcoming react+d3js es6 book .
3 presentation components
we’ll start with the presentation components because they’re the least complicated. to render a collection of particles, we need:
-
a stateless
particle
-
a stateless
particles
-
a proper
app
none of them contain state, but
app
has to be a proper component so that we can use
componentdidmount
. we need it to attach d3 event listeners.
the
particle
component is a circle. it looks like this:
|
it takes
x
and
y
coordinates and returns an svg circle.
the
particles
component isn’t much smarter–it returns a list of circles wrapped in a grouping element, like this:
|
note that
key={particle.id}
part. react complains endlessly without it. i think it’s used to tell similar components apart, which makes the fancy algorithms work.
cool. given an array of
{id, x, y}
objects, we can render our svg circles. now comes our first fun component: the
app
.
app
takes care of rendering the scene and attaching d3 event listeners. the rendering part looks like this:
|
there’s more going on, but the gist is that we return a
<div>
with a
header
, a
footer
, and an
<svg>
. inside
<svg>
, we use
particles
to render many circles. don’t worry about the header and footer; they’re text.
notice that the core of our rendering function only says “put all particles here, please” . there’s nothing about what’s moved, what’s new, or what’s no longer needed. we don’t have to worry about that.
we get a list of coordinates and naively render circles. react takes care of the rest. if you ask me, that’s the real magic here.
oh, and we call
startticker()
when a user clicks on our scene. no reason to have the clock running
before
any particles exist.
d3 event listeners
to let users generate particles, we have to wire up those functions we mentioned in
proptypes
. it looks like this:
|
there are some events to think about:
-
mousedown
andtouchstart
turn on particle generation -
mousemove
andtouchmove
update the mouse location -
mouseup
,touchend
, andmouseleave
turn off particle generation
then you have
updatemousepos
and
updatetouchpos
, which use d3’s magic to calculate new
(x, y)
coordinates relative to our svg element. the particle generation step uses this data as each particle’s initial position.
yes, it’s complicated.
remember, react isn’t smart enough to figure out mouse position relative to our drawing area. react knows that we clicked a dom node. d3 does the magic to find exact coordinates.
for touch events, we only consider the first touch’s position. we could let users shoot particles out of multiple fingers at once, but there’s enough going on as it is.
that’s it for the rendering and the user events. 107 lines of code .
6 actions
redux actions are a fancy way of saying “yo, a thing happened!” . they’re functions you call to get structured metadata about what’s up.
we have 6 actions. the most complicated one looks like this:
|
it tells the system to create
n
particles at
(x, y)
coordinates. you’ll see how that works when we look at the reducer, and you’ll see how it triggers when we look at the container.
actions
must
have a
type
. reducers use the type to decide what to do.
our other actions
are
ticktime
,
tickerstarted
,
startparticles
,
stopparticles
, and
updatemousepos
. you can guess what they mean.
1 container component
containers are react components much like the presentation bits. unlike presentation components, containers talk to the redux data store.
i’m not sure this separation is strictly necessary, but it does make your code look nice. you have functionally clean presentation components that turn properties into elements, and dirty, dirty containers that talk to the outside world.
you can think of them as data store monads if it helps.
the gist of our
appcontainer
looks like this:
|
we import stuff, then we define
appcontainer
as a proper react
component
. we need to use lifecycle methods, which aren’t available in stateless components.
there are three important parts in this code:
-
we wire up the store in
componentdidmount
andcomponentwillunmount
. subscribe to data changes on mount, unsubscribe on unmount. -
when rendering, we assume the store is our context, use
getstate()
, then render the component we’re wrapping. in this case, we render theapp
component. -
to get the store as our context, we
have to
define
contexttypes
. it won’t work otherwise. this is deep react magic.
contexts are nice because they let us implicitly pass properties. the context can be anything, but redux prefers it be the store. that contains everything about application state–both ui and business data.
that’s where the monad comparison comes from… my foray into haskell may have broken me. i see monads everywhere.
in case, you were wondering, that
{::this.startticker}
syntax comes from es2016. it’s equivalent to
{this.startticker.bind(this)}
. enable
stage-0
in your babel config to use it.
appcontainer talks to the store
great, you know the basics. now we need to define those callbacks so
app
can trigger actions. most are boilerplate-y action wrappers. like this:
|
that’s boilerplate. the action function gives us that
{type: ..}
object, and we dispatch it on the store.
when that happens, redux uses our reducer to create a new instance of the state tree. we’ll talk more about that in the next section.
first, we have to look at the
startticker
callback. it’s where our magic begins.
|
oof. don’t worry if you don’t "get" this immediately. it took me a few hours to create.
this drives our animation loop. some might call it the game loop.
it dispatches the
ticktime
action on every
requestanimationframe
. every time the browser is ready to render, we get a chance to update our redux data store. in theory, that’s 60 times a second, but it depends on many factors. look it up.
startticker
updates the store in two steps:
-
check
tickerstarted
flag and only start the ticker if it hasn’t been started yet. this way we don’t try to run multiple animation frames per render frame. as a result, we can be naive about bindingstartticker
toonmousedown
. -
create a
ticker
function that generates particles, dispatches theticktime
action, and recursively calls itself on everyrequestanimationframe
. we check thetickerstarted
flag every time so we can potentially stop time.
yes, that means we are asynchronously dispatching redux actions. it’s okay; that sort of just working ™ is one of the main benefits of immutable data.
the
maybecreateparticles
function itself isn’t too interesting. it gets
(x, y)
coordinates from
store.mousepos
, checks the
generateparticles
flag, and dispatches the
createparticles
action.
that’s the container. 83 lines of code .
1 reducer
sweet. with the actions firing and the drawing done, it’s time to look at the entire logic of our particle generator. we’ll get it done in just 33 lines of code and some change.
ok. honestly? it’s a bunch of change. but the 33 lines that make up
create_particles
and
time_tick
changes are the most interesting.
all of our logic goes in the reducer.
dan abramov says
to think of reducers as the function you’d put in
.reduce()
. given a state and a set of changes, how do i create the new state?
a simplistic example would look like this:
|
for every number, take the previous sum and add the number.
our particle generator is a more complicated version of that. it takes the current application state, incorporates an action, and returns the new application state. to keep things simple, we’ll put everything into the same reducer and use a big
switch
statement to decide what to do based on
action.type
.
in bigger applications, we’d split this into multiple reducers, but the base principles stay the same.
let’s start with the basics:
|
this is our reducer.
we started with the gravity constant and two random generators. then we defined the default state:
- an empty list of particles
- a particle index, which i’ll explain in a bit
- the number of particles we want to generate on each tick
- default svg sizing
-
and the two flags and
mousepos
for the generator
our reducer doesn’t change anything yet. it’s important to always return at least an unchanged state.
for most actions, our reducer updates a single value. like this:
|
even though we’re only changing values of boolean flags and two-digit arrays, we have to create a new state . always create a new state. redux relies on application state being immutable.
that’s why we use
object.assign({}, ...
every time. it creates a new empty object, fills it with the current state, then overwrites specific values with new ones.
either do that every time or use a library for immutable data structures. that might work better, but i haven’t tried it yet.
the two most important state updates–animation tick and create particles–look like this:
|
that looks like a bunch of code. sort of. it’s spread out.
the first part–
create_particles
–copies all current articles into a new array and adds
action.n
new particles to the beginning. in my tests, this proved smoother than adding particles at the end. each particle starts life at
(action.x, action.y)
and gets a random movement vector.
this is bad from redux’s perspective. reducers are supposed to be pure functions, and randomness is inherently impure. i think it’s fine in this case.
the other possible approach would shove this logic into the action. that has benefits, but makes it harder to see all logic in one place. anyway…
the second part–
time_tick
–doesn’t copy the particles (although maybe it should). arrays are passed by reference, so we’re mutating existing data either way. this is bad, but it’s not
too
bad. it definitely works faster.
we filter out any particles that have left the visible area. for the rest, we add the movement vector to their position. then we change the
y
part of the vector using our
gravity
constant.
that’s an easy way to implement acceleration.
that’s it. our reducer is done. our particle generator works. our thing animates smoothly.

important discoveries
building this particle generator in react and redux, i made three important discoveries:
- redux is much faster than i thought . you’d think creating a new copy of the state tree on each animation loop was crazy, but it works well. we’re probably creating only a shallow copy for the most part, which explains the speed.
- adding to javascript arrays is slow . once we hit about 300 particles, adding new ones becomes visibly slow. stop adding particles and you get smooth animation. this indicates that something about creating particles is slow: either adding to the array, or creating react component instances, or creating svg dom nodes.
- svg is also slow . to test the above hypothesis, i made the generator create 3000 particles on first click. the animation speed is terrible at first and becomes okayish at around 1000 particles. this suggests that shallow copying big arrays and moving existing svg nodes around is faster than adding new stuff. here’s a gif:

3000 particles is too many
there you go. animations done with react, redux, and d3. new superpower? maybe.
here’s the recap:
- react handles rendering
- d3 calculates stuff
- redux handles state
- element coordinates are state
-
change coordinates on every
requestanimationframe
- magic
don’t forget: this and other cool stuff will show up in the new version of my book, react+d3 , that’s launching this month.

thanks to primoz cigler, sigurt bladt dinesen, and will fanguy for reading drafts of this article
related
you should follow me on twitter, here .
Published at DZone with permission of Swizec Teller, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments