React's "setState" and "this"

setState and this
WIP

One of the great things about React is that it’s just JavaScript. Class components are no exception - they’re just JavaScript classes. This means that they can define a constructor, and they have access to a this object — sometimes called the component instance.

Which brings me to the most important thing about class components.

When React encounters an element for a class component, it will instantiate a component instance, and keep it around for the next time that the element needs to be rendered.

#Component instances persist over time

Class components can use this to store information — or state — for subsequent renders.

In contrast, function components only have access to their props.

Class components can do things that function components can’t. For example, you could keep track of the number of times that a class component has been rendered by incrementing an instance variable.

index.js
import React from 'react'
import ReactDOM from 'react-dom'

class RenderCounter extends React.Component {
  constructor() {
    super()
    this.renderCount = 0
  }

  render() {
    let newRenderCount = ++this.renderCount
    let message = `rendered ${newRenderCount} times`
    return <div>{message}</div>
  }
}

window.setInterval(
  () => 
    ReactDOM.render(
      <RenderCounter />,
      document.getElementById('root')
    ),
  1000
)
Build In Progress

And there’s one more special thing that class components can do.

#The setState method

The first rule of class components is that they must extend React.Component. As a result, your classes will inherit a bunch of methods. And the most important inherited method, by far, is setState.

When you call this.setState(newState) from a class component, React will schedule a merge of newState into any existing value of this.state.

But React will also do something else. See if you can figure out what it is that setState does by playing with this demo…

index.js
import React from 'react'
import ReactDOM from 'react-dom'

class InputField extends React.Component {
  constructor(props) {
    super(props)
    this.state = { value: 'you can edit me!' }

    // This is required to make the event handler work -
    // we'll discuss why in the next lesson!
    this.handleChange = this.handleChange.bind(this)
  }

  render() {
    return (
      <input
        value={this.state.value}
        onChange={this.handleChange}
      />
    )
  }

  handleChange(e) {
    this.setState({
      value: e.target.value,
    })
  }
}

ReactDOM.render(
  <InputField />,
  document.getElementById('root')
)
Build In Progress

Did you figure it out? Once you think you know, or you’re completely stuck, check your answer below.

I mentioned earlier that there are two ways of re-rendering, and now you know the second way!

  1. You can re-render the entire app with ReactDOM.render()
  2. You can re-render a single component by updating its state.

As you’ve probably figured out, this is pretty darn important. We’ll discuss why in a moment. But first, I must give you the fine print.

#The fine print

Using setState from event handlers is simple. But when using it in other contexts, there are some things you’ll need to watch out for.

#Changes are not immediate

Calling this.setState(...) won’t immediately update this.state. Instead, it’ll schedule an update which will occur sometime before the next repaint, but probably not before the method that called setState finishes executing. If you need the current value of this.state to compute the next value, pass an updater function. And if you need to know when the update completes, you can pass a callback.

#Setting state in the constructor

If you don’t set this.state, it defaults to undefined. So if your component makes use of state, you’ll make your life a whole lot easier by setting an initial value in the constructor.

There’s one catch: you can’t call setState within the constructor. Instead, use simple assingment:

constructor() {
  this.state = {
    // initial state
  }
}

#You can’t setState within render

A class component’s render method is for rendering. All it should do is figure out how to convert this.props and this.state into a React Element. That’s it.

But with this said, sometimes you do need to set some state after each render. And React provides a way to do this, called lifecycle methods. You won’t often need these, but we will touch on them later.

#If you don’t need state, don’t use it

State adds complexity to your component. And while necessary complexity is… necessary, unnecessary complexity is going to come back and haunt you.

Practically speaking, this means that if you can compute something from props instead, you should do so. The only time you should cache a computation in state is when you’ve measured a significant performance benefit from doing so.

Of course, when you do need setState, it is invaluable. And now that you’ve read the fine print, let’s talk about why you’ll actually need it.

#Why use state?

So far, this course has focused exclusively on function components. And with good reason, too! They’re simple, and they let you accomplish a great deal.

But consider this: the whole reason that you’d want to use components — indeed, the reason for using React itself — is that it lets you encapsulate and re-use functionality. Function components are great at encapsulating presentation. But to encapsulate behavior, you’ll need class components and state.

The ability to store state opens up a world of possibilities. With class components, you can create:

  • Animated components that that re-render themselves.
  • Routing components that update when the browser’s location does.
  • Components that interact with APIs and store received data.

And as you’ve probably guessed, storing user input allows you to build forms!

Progress to next section.