1. Learn
  2. Learn React By Itself

Rendering and Re-rendering

Let's pop the hood and see how React actually updates the DOM. And animate a fractal tree while we're at it...

James K Nelson

James is the editor of React Armory, and has been creating things with JavaScript for over 15 years.

Read more by JamesFollow @james_k_nelson on Twitter

In part two of this guide, you created and rendered a bunch of React Elements. In fact, you created an entire fractal tree from React Elements. But so far, your elements don’t move or change. And I promised you that this tree would sway. So let’s animate it!


// You can see how `TreeBox` works in part two
import { TreeBox } from 'react-armory-pythagoras-tree'

let lean = -0.10
let heightFactor = 0.37

ReactDOM.render(
  React.createElement(TreeBox, {
    level: 0,
    totalLevels: 5,
    heightFactor: heightFactor,
    lean: lean,
    size: 100,
  }),
  document.getElementById('app')
)

To start, take a look at the lean variable in the above example. This prop controls how much the tree leans to one side.

To get a feel for this, try editing lean to other values like -0.2 and 0.1, and see what happens. Notice how decreasing lean moves the tree to the left, while increasing it moves it to the right.

If you were to continuously update the value of lean, cycling from negative to positive numbers, you could manually make the tree sway! But what if you’d like the animation to be smooth and automatic?

Changing props

Given that the lean prop for TreeBox is stored in a variable, perhaps you could use setInterval to periodically update lean? The code would look something like this:

setInterval(function, periodInMilliseconds) will repeatedly call the passed in function, with a periodInMilliseconds gap between each call.

Read more at MDN.

let time = 0
setInterval(() => {
  time += 1
  lean = 0.1*Math.sin(time / 50)
}, 50)

But despite what you may expect from a library called “React”, nothing will change! You can confirm this by copying the setInterval call into the above example.

The thing about React is that it only renders when you tell it to. So in order to display something new – like your animation’s next frame – you’ll need to order a re-render. But how?

Displaying changes

Actually, while you may not have realized it at the time, you’ve already seen the method for ordering a re-render! Do you know what method it is? Have a think about it, then check your answer by clicking the box below.

To update your element's props, just render it again from scratch using `ReactDOM.render()`

Does this make sense? If not, maybe seeing it in action will help. And the best way to see it in action is to do an exercise!

In this exercise, your task is to add a call to ReactDOM.render to the below editor. You should place the call such that each frame renders a new value of lean.

If you get stuck, you can view the solution below. But make sure to give it a shot first!

import { TreeBox } from 'react-armory-pythagoras-tree'

let heightFactor = 0.37
let lean = 0
let time = 0

setInterval(() => {
  lean = 0.1*Math.sin(time / 50)
  time += 1
}, 40)
import { TreeBox } from 'react-armory-pythagoras-tree'

let heightFactor = 0.37
let lean = 0
let time = 0

setInterval(() => {
  lean = 0.1*Math.sin(time / 50)
  time += 1

  ReactDOM.render(
    React.createElement(TreeBox, {
      level: 0,
      totalLevels: 5,
      heightFactor: heightFactor,
      lean: lean,
      size: 100,
    }),
    document.getElementById('app')
  )
}, 40)

Did you manage to animate the tree? Then congratulations! You now have proof that ReactDOM.render can update elements!

But isn’t that a bit weird? After all, it is called render – not update! We’ll clear this up in a moment, but before we do we’ll need to review React’s basics.

What does render do?

A DOM Node is a JavaScript object that represents a single HTML element. You can use this object to read and modify the associated HTML element.

Read more at MDN.

The ReactDOM.render function renders a React Element to a DOM Node. But what is meant by render? This is an interesting question, because the answer depends on the situation:

  • On the first call to ReactDOM.render(reactElement, domNode), the DOM Node’s content will be replaced with the content specified by the React Element.

  • But if the DOM Node already contains content rendered by React, this content will be updated to reflect the new React Element.

React also allows you to make the first render call on the server. This will slightly speed up your app at the expense of a massive increase in complexity.

Avoid server-side rendering unless you have a bloody good reason for doing so.

React is pretty clever about reusing DOM nodes instead of replacing them. In fact, this is a big part of React’s reputation for performance.

For example, if you were to render exactly the same React Element twice:

var tree = React.createElement(TreeBox, {
  level: 0,
  totalLevels: 5,
  heightFactor: heightFactor,
  lean: lean,
  size: 100,
})

ReactDOM.render(tree, document.getElementById('app'))
ReactDOM.render(tree, document.getElementById('app'))

The second call to render would do absolutely nothing. Nothing, that is, unless you are rendering an array of elements without assigning a unique key prop to each. And that’s where that key prop from part 1 comes in.

But why are keys important when rendering arrays? To find out, let’s pop the hood and take a look.

How render works

The first time that your application calls ReactDOM.render(), the outcome is pretty simple. React just walks through the supplied React Element and creates corresponding DOM Nodes under the Node that you passed in.

But as I mentioned above, the second and subsequent calls don’t modify previously rendered DOM Nodes unless the corresponding React Element objects have changed. How does this work?

In order to decide what to change, React recursively compares the new React Element with the previous one. It uses a number of rules to decide what to do:

  • Elements with differing types are trashed and re-rendered
  • Elements with differing props or children are re-rendered in place
  • Identical elements which have been re-ordered within an array are moved to reflect the new order

Arrays and keys

There’s one catch - when React encounters an array of elements with identical type and props, despite looking identical from the outside, it cannot know that they are really identical. This is because elements can have internal state (for example, whether a checkbox is checked or not). This becomes a problem when React goes to re-render those elements, as it cannot tell one from another - and thus doesn’t know if their order within the array has changed.

To demonstrate, this example renders an array of checkbox elements. Try clicking a checkbox and then clicking the “Reverse” button. Notice how nothing happens.

import ReversibleList from 'react-armory-reversible-list'

const checkboxes = [
  React.createElement('input', { type: 'checkbox' }),
  React.createElement('input', { type: 'checkbox' }),
  React.createElement('input', { type: 'checkbox' }),
  React.createElement('input', { type: 'checkbox' }),
]
const reverser = React.createElement(ReversibleList, {}, checkboxes)

// Try clicking some checkboxes, then click "Reverse" in the Preview pane
ReactDOM.render(reverser, document.getElementById('app'))

But while React can’t distinguish between identical elements in an array, we can always give it some help! And that is what the key prop is for; when available, it gives React a way track elements between reorders.

To see this in action, try adding unique key props to each of the checkboxes above. Your keys can be strings or numbers – the only thing that matters is that they’re unique. Once you’ve added these, click a checkbox and then click “Reverse”; the checked boxes should now move as expected!

If you’re having any trouble with this, you can see the solution below. But do give it a shot first!

import ReversibleList from 'react-armory-reversible-list'

const checkboxes = [
  React.createElement('input', { type: 'checkbox', key: '1' }),
  React.createElement('input', { type: 'checkbox', key: '2' }),
  React.createElement('input', { type: 'checkbox', key: '3' }),
  React.createElement('input', { type: 'checkbox', key: '4' }),
]
const reverser = React.createElement(ReversibleList, {}, checkboxes)

ReactDOM.render(reverser, document.getElementById('app'))

While the above example may demonstrate what the key prop does and why it is needed, it also raises a question: how does React know to reverse the checkboxes when the user clicks the button? And to find out, we’ll need to talk about events.

Continue to: Everything About Events

Join React Armory,
Get Cool Stuff

Exclusive access to resources.

Early access to new content.

No spam, ever.

React Armory is growing. Join in to be the first to experience new lessons, examples and resources.