Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

React Quickly: Working With States

DZone's Guide to

React Quickly: Working With States

Azat Mardan provides us with an overview of working with states in React, giving us a glimpse into his book, React Quickly.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Image title


Working With States

by Azat Mardan

Save 37% off React Quickly with code fccmardan





Note:
The source code for the examples in this article can be found in the ch04 folder of the GitHub repository azat-co/react-quickly. And some demos can be found at http://reactquickly.co/demos.

To be able to work with state, we need to know how to access the values, update them and how to set the initial values. Let's start with accessing state in React components.

Accessing States

The state object is an attribute of a component and can be accessed with  this  reference, e.g.,  this.state.name . We can access and print variables in JSX with curly braces  {}. Similarly, we can render  this.state  (as any other variable or a custom component class attributes) inside of  render().

For example,  {this.state.inputFieldValue} . This syntax is like accessing properties with  this.props.name.

Let's go ahead and try to implement a clock (Figure 1). The goal is to have a self-containing component class anyone can import and use in their application without having to jump through hoops every time. The clock must render the current time.

Image title

Figure 1: Clock component shows current time in digital format—updated every second


The structure of the Clock project is as follows:

/clock
  - index.html
  /jsx
    - script.jsx
    - clock.jsx
  /js
    - script.js
    - clock.js
    - react-15.0.2.js
    - react-dom-15.0.2.js


I'm using Babel CLI with a watch  -w  and a directory flag  -d  to compile all source JSX files from  clock/jsx  to a destination folder  clock/js  and recompile on changes. Moreover, I’ve saved the command as an npm script in my  package.json  in a parent folder called  ch04  in order to run  npm run build-clock  from  ch04:

"scripts": {
    "build-clock": "./node_modules/.bin/babel clock/jsx -d clock/js -w"
},


Obviously, time is always changing (for good or for bad). Because of that, we'll need to update the view using state. We name it currentTime and try to render this state as shown in Listing 1.

Listing 1: Rendering State in JSX

class Clock extends React.Component {
  render() {
    return <div>{this.state.currentTime}</div>
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('content')
)


We'll get Uncaught  TypeError: Cannot read property ' currentTime ' of null. Normally, JavaScript error messages are as helpful as a glass of cold water to a drowning man. It's good that in this case JavaScript gives us a helpful error message. This one means we don't have any value for  currentTime. Unlike props, states aren’t set on a parent. We can't  setState  in  render()  either, because it'll create a circular (setState->render->setState...) loop and, in this case, React will throw an error.

Setting the Initial State

You’ve seen that before using a state data in  render() , we must initialize it. To set the initial state, use  this.state  in the constructor with your ES6 class React.Component syntax. Don't forget to invoke  super()  with properties, otherwise the logic in parent ( React.Component ) won’t work.

class MyFancyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {...}
  }
  render() {
    ...
  }
}


Developers can add other logic when setting the initial state. For example, we can set the value of currentTime using new Date(). We can even use  toLocaleString() to get the proper date and time format in the users location:

Listing 2: Clock Component Constructor (ch04/clock)

class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.state = {currentTime: (new Date()).toLocaleString()}
  }
  ...
}


The value of  this.state  must be an object. We won't get into a lot of details about ES6  constructor() , because there’s information in the ES6 cheatsheet. The gist is that, as with other OOP languages,  constructor()  is invoked when an instance of this class is created. The constructor method name must be constructor. Think about it as an ES6 convention. Furthermore, if you create  constructor()  method, you almost always need to invoke  super()  inside of it, otherwise the parent's constructor won't be executed. On the other hand, if you don't define  constructor()  method, then the call to  super()  is assumed.

Class Attributes

Hopefully, the TC39 (people behind the ECMAScript standard) will add attributes to the class syntax in future versions of ECMAScript! This way, developers can set state not only in constructor, but in the body of the class:

class Clock extends React.Component {
  state = {
    ...
  }
}


The proposal is called class instance fields or class properties, but as of July, 2016 it's only available with transpilers: Babel, Traceur, or TypeScript, which means no browser will run this feature natively. Check out the current compatibility of class properties in ECMAScript Compatibility Table.)

Here,  curentTime  is an arbitrary name, and we'll need to use the same name later when accessing and updating this state. You can name your state any way you want if you refer to it later using this name.

The state object can have nested objects or arrays. Look at this example, where I add an array of my books to the state:

class Content extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      githubName: 'azat-co',
      books: [
        'pro express.js',
        'practical node.js',
        'rapid prototyping with js'
      ]
    }
  }
  render() {
    ...
  }
}


The  constructor()  method will be called once, when a React element is created from this class. This way, we can set state directly by using  this.state , in the  constructor()  method. Avoid setting and updating state directly with   this.state  =  ... anywhere else, because it might lead to unintended consequences.

>> With React's own  createClass()  method to define a component, you'll need to use  getInitialState() . <<

This will only get us the first value, which becomes outdated quickly; in one second. What's the point of a clock that doesn’t show the current time? Luckily, there's a way to update the state.

Updating States

We change the state with  this.setState(data, callback)  class method. When this method is invoked, React merges the data with current states and calls  render(). After that, React calls  callback.

Having the callback in setState() is important because the methods work asynchronously. If you're relying on the new state, you can use callback to make sure this new state is available. If you rely on a new state without waiting for  setState()  to finish its work, i.e., working synchronously with asynchronous operation, then you might have a bug when the state is still an old state.

We've rendered the time from a state, we also set the initial state, but we need to update the time every second, right? We can use a browser timer function <code>setInterval()</code> which will execute the state update every n milliseconds. The  setInterval() method is implemented in virtually all modern browsers as a global, which means developers can use it without any libraries or prefixes.

setInterval(()=>{
  console.log('Updating time...')
  this.setState({
    currentTime: (new Date()).toLocaleString()
  })
}, 1000)


To kick-start the clock, we need to invoke  setInterval()  once. We can create a method  launchClock()  to do that. We'll call  launchClock()  in constructor. The final Clock might look like the one shown in Listing 3.

Listing 3: Implementing Clock with React State and setInterval() (ch04/clock/jsx/clock.jsx).

class Clock extends React.Component {
  constructor(props) {
    super(props)
    this.launchClock()      <1>
    this.state = {
      currentTime: (new Date()).toLocaleString()        <2>
    }
  }
  launchClock() {
    setInterval(()=>{
      console.log('Updating time...')
      this.setState({
        currentTime: (new Date()).toLocaleString()      <3>
            })
    }, 1000)        <4>
  }
  render() {
    console.log('Rendering Clock...')
    return <div>{this.state.currentTime}</div>      <5>
  }
}


<1> Trigger launchClock()  

<2> Set initial state to current time

<3> Update state with current time every second

<4> Bind context to reference the component instance

<5> Render state

You can use  setState()  anywhere, not only in  launchClock()  (which is invoked by  constructor ), as shown in the example. Typically,  setState()  is called from the event handler or as a callback for incoming data or data updates.

>>Changing a state value in your code like this  this.state.name= 'new name't won't do any good. It won't trigger a re-render and a possible real DOM update, which we want. For the most part, changing state directly without  setState  is an anti-pattern and should be avoided.<<

It's important to note that  setState()  updates only the states that you pass it (partial or merge, but not a complete replace). It's not replacing the entire state object each time. If you have three states and then change one, the other two remain unchanged. In the example below,  userEmail  and  userId  will remain intact:

constructor(props) {
  super(props)
  this.state = {
    userName: 'Azat Mardan',
    userEmail: 'hi@azat.co',
    userId: 3967
  }
}
updateValues() {
  this.setState({userName: 'Azat'})
}


If your intention is to update all three states, then you need to do it explicitly by passing the new values for these states to setState(). Another method sometimes seen in old React code, but which no longer works and was deprecated, is  this.replaceState() method. As you can guess from the name, it replaced the entire state object with all its attributes.

Keep in mind that  setState()  triggers render(). It works in most cases. In some edge-case scenarios, when the code depends on external data, you can trigger re-render with  this.forceUpdate(), but this approach should be avoided because it relies on external data, rather than the state, making components more fragile and dependent upon external factors (tight coupling).

As mentioned before, you can access the state object with  this.state. If you remember, we output values with curly braces ( {} ); therefore, to declare a state property in the view (render's return statement), apply  this.state.NAME .

React magic happens when you use state data in the view (for example, to print, in if/else, as a value of an attribute, or as a child's property value) and then give  setState()  new values. Boom! React updates the HTML for you. You can observe it in your DevTools console. It should show cycles of  updating...  and then  rendering...  And, the best part is that ONLY the minimum required DOM elements are affected.

Binding this in JavaScript

In JavaScript, this mutates (changes) its value depending on where a function is called from. To ensure that this refers to our component class, we need to bind the function to the proper context (this value is our component class).

If you're using ES6+/ES2015+ as I do here in this article, then you can use fat arrow function syntax to create a function with an autobinding:

setInterval(()=>{
  this.setState({
    currentTime: (new Date()).toLocaleString()
  })
}, 1000)


Autobinding means that the function created with a fat arrows will get the current value of  this  which is, in our case,  Clock.

The manual approach is to use  bind(this)  method on the closure:

function() {...}.bind(this)
Or for our Clock:
setInterval(function(){
  this.setState({
    currentTime: (new Date()).toLocaleString()
  })
}.bind(this), 1000)


This behavior isn’t exclusive to React. The this keyword mutates inside a function's closure, and we need to either bind it or save the context ( this ) value for later use.

Typically, we'll see variables like  self ,  that , or  _this  used to save the value of the original this. Most of you probably saw statements like the following:

var that = this
var _this = this
var self = this


The idea is straightforward; you create a variable and use it in the closure instead of referring to  this . The new variable won't be a copy, but a reference to the original  this value. Here's our  setInterval():

var _this = this
setInterval(function(){
  _this.setState({
    currentTime: (new Date()).toLocaleString()
  })
}, 1000)


We have our clock and it works (Figure 2). Tadaaa!

Image title


Figure 2: The Clock is Ticking

One thing before we move on. You can see how React is reusing the same DOM <div> element and only changes the text inside it. Go ahead and use DevTools to modify the CSS of this element. I added a style to make text blue:  color: blue  as shown in Figure 3. It created an inline style, not a class. The element and its new inline style stayed the same (blue) as the time kept on ticking.

Image title

Figure 3: React is updating time as only text, not the element div (manually added color: blue)


React will only update the inner HTML (the content of the second <div> container); <div>and all elements on this page, remain intact. Neat. ;-)

States and Properties

States and properties are both attributes for a class, meaning they’re  this.state  and  this.props. That's the only similarity! One of the main differences between properties and state is that the former is immutable and the latter is mutable.

Another difference between properties and states is that we pass properties from parent components, as we define states in the component itself, not its parent. The philosophy here is that you can only change the value of a property from the parent, not the component itself. Properties determine the view upon creation and then they stay static (they don't change). The state, on the other hand, is set and updated by the object itself.

Props and states serve different purposes, but both are accessible as attributes of component class, and both help developers to compose components with different representation (view). Numerous differences between props and states exist when it comes to component lifecycle. Think about props and states as inputs for a function that produces different outputs. Those outputs are views. You can have different UIs (views) for each set of props and states (Figure 4).

Image title

Figure 4: New values for props and states can change the UI, but for props the new values come from a parent and for state from the component itself


Not all components need to have state. Let’s take a look at how to use properties with stateless components.

Stateless Components

The concept of a stateless component is a component that has no states, no components, nor any other React lifecycle events/methods. The purpose of a stateless component is to render the view. The only thing it can do is take properties and do something with them—a simple function with an input (property) and an output (UI element).

The benefit of using stateless components is they’re predictable, because we’ve one input which determines the output. Predictability means they’re easier to understand, maintain, and debug. In fact, not having a state is the most desired React practice—the more stateless components you use and the less ‘stateful’ they are, the better.

This Hello World is a good example of a stateless component (listing 4).

Listing 4 (ch03/hello-js-world-jsx/jsx/script.jsx)

class HelloWorld extends React.Component {
  render() {
    return <h1 {...this.props}>Hello {this.props.frameworkName} world!!!</h1>
  }
}


To have a smaller syntax for stateless components, React provides us with a function style. We create a function that take properties as an argument and return the view. A stateless component renders like any other component. For example, the HelloWorld component can be re-written as a function that returns <h1>:

const HelloWorld = function(props){
  return <h1 {...props}>Hello {props.frameworkName} world!!!</h1>
}


Note:

Yes. You can use ES6+/ES2015+ arrow functions for stateless components. The following snippet is analogous to the snippet above (return can be omitted too, but I like to have it):

const HelloWorld = (props)=>{
  return <h1 {...props}>Hello {props.frameworkName} world!!!</h1>
}


As can be seen, developers can also define functions as React components when there's no need for a state. To create a stateless component simply define it as a function. Another example in which Link is a stateless component:

function Link (props) {
  return <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>
}
ReactDOM.render(
  <Link />,
  document.getElementById('content')
)


There’s no need for autobinding, but we can use the fat arrows function syntax for brevity (when there's a single statement, the notation can be a one-liner):

const Link = props=> <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>


In a stateless component, we can't have a state, but we can have two properties:  propTypes  and  defaultProps. We set them on the object:

function Link (props) {
  return <a href={props.href} target="_blank" className="btn btn-primary">{props.text}</a>
}
Link.propTypes = {...}
Link.defaultProps = {...}


We also can't use references  refs  with stateless functions. If you need to use  refs , you can wrap a stateless component in a normal React component.

That’s all for this article, for more on React and its myriad of uses, download the free first chapter of React Quickly and use code fccmardan to save 37% off your purchase.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
javascript ,react ,states ,web dev

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}