Register to get access to free programming courses with interactive exercises

State JS: React

You've already thoroughly studied JSX and the non-interactive way of working with React components. With this lesson, we begin the most important part: interaction with the user. The key concepts that will be covered in this lesson are events and state. Example:

See the Pen js_react_state_example by Hexlet (@hexlet) on CodePen.

The components that were created in the course before were stateless, i.e., they didn't contain any state and could only render properties passed to them. The component in the example above is stateful because it stores the state of the current time inside itself. In order:

  1. Inside the component, in the constructor, an initial state is defined with which the component will be initialized after rendering. The only requirement React has for the state is the data type: it must be an object. What is stored inside is determined by the application itself.

The way to set the initial state is as follows:

class Clock extends React.Component {
  constructor(props) {
    super(props); // always mandatory
    this.state = { date: new Date() };
  }
}

Note that this is the only place where state can be changed directly (or rather, created). In all other places this.state must be read-only! We'll see more about it further on.

  1. The render function uses data from state to render. No surprises here.
  2. A click handler is attached to the button. Unlike HTML, a function is passed to the onClick property and will be called automatically when the event is triggered. The current date is determined, and the new state is set inside the handler. Once again: it's extremely important not to change state directly. To set a new state, React has a function called setState. Calling it causes the component to eventually be rendered again. This doesn't happen immediately, i.e., setState works asynchronously and its internal magic tries to optimize the rendering process.

Another important point to note is how the handleClick function is defined. Since we're working with a class, it would be logical to use this style of definition:

class Clock extends React.Component {
  handleClick() {
    this.setState({ date: new Date() });
  }
}

But this approach doesn't work well in React for two reasons.

The first is that handlers are called asynchronously, and methods in classes are normal functions with late binding. So you can't just attach a handler like that because it'll lose this. With this definition, you'll have to write this kind of code all the time: onClick={this.handleClick.bind(this)} or this: onClick={() => this.handleClick()}.

The second reason has to do with performance. Both of the previous handler pass examples spawn new handlers for every call to the render function (since functions are compared by reference, not content), and for React this is critical. Therefore, the correct way to define it is with the arrow function:

class Counter extends React.Component {
  handleClick = () => {
    this.setState({ date: new Date() });
  }
}

Another example:

See the Pen by Hexlet (@hexlet) on CodePen.

It is logical to expect the counter to increment by 2 with each click, but this does not happen. As mentioned above, this.setState is not executed by React immediately. Consequently, a situation may arise where the state or props have changed by the time the state change is executed. For such cases where the new state is determined based on the previous state (or props), setState has the ability to take a function instead of an object:

this.setState((state, props) => {
  const { count } = state;

  return { count: count + 1 };
})

Try to rewrite the counter example using the described setState form.

Basically, the mechanisms described above open almost all the doors. You can now easily create interactive components and animate your UI. Everything else is finesse provided for different situations.

Initializing

Imagine that in the component created above, you need to initialize the counter with the count property passed from outside. And only put 0 in its absence. To solve this problem you need to add two things:

  1. Use the count property as the initial counter value.
  2. Add a default value for the count property.

See the Pen js_react_state_init by Hexlet (@hexlet) on CodePen.

setState

In the following example, two buttons are implemented, each of which controls its own state.

See the Pen js_react_state_set by Hexlet (@hexlet) on CodePen.

In this example, the state object includes two properties: count for one button and primary for the other. The main trick of this example is the process of updating the state:

// first button 
this.setState(({ count }) => ({ count: count + 1 }));

// second button
this.setState(({ primary }) => ({ primary: !primary }));

The setState function replaces the values of the keys in the previous state with the values of the same keys in the new state. What was not returned inside the function is not touched. In fact, the old state and the new state are merged. In practice, this behavior is extremely convenient, otherwise you would have to do the merge work by hand every time.

Structure of the state object

There are many ways to organize data within a state. Most likely, you will want to store them in some way:

const blogPosts = [
  {
    id : "post1",
    author : {username : "user1", name : "User 1"},
    body : "......",
    comments : [
      {
        id : "comment1",
        author : {username : "user2", name : "User 2"},
        comment : ".....",
      },
      {
        id : "comment2",
        author : {username : "user3", name : "User 3"},
        comment : ".....",
      }
    ]
  },
  {
    id : "post2",
    author : {username : "user2", name : "User 2"},
    body : "......",
    comments : [
      {
        id : "comment3",
        author : {username : "user3", name : "User 3"},
        comment : ".....",
      },
    ]
  }
  // and repeat many times
]

In this approach, entities that are dependent on others are inside. Taking the example above, this means that each post contains both an author and a list of comments, and each comment, in turn, contains a related entity of the same author. With this approach, it turns out that the state is a dependency tree. Although this way of organization seems quite natural, it is extremely difficult to work with. First, the same data will start to be duplicated in different places and you will have to synchronize changes in it, which creates space problems in the empty space. Second, updates to such data (especially in an immutable style) become complicated and verbose. Third, since the entire state is one big chunk, any update will cause it to be copied out completely, which can be an expensive operation (depending on the size of the state and the number of updates per unit time).

A general recommendation that React developers give is to make the structure as flat as possible, similar to the way data is stored in a database. And preferably in a well-normalized form. In other words, you don't want to duplicate data in the state. An example of how to do this properly:

const state = {
  articles: [/*...*/],
  comments: [/*...*/],
}

Hexlet Experts

Are there any more questions? Ask them in the Discussion section.

The Hexlet support team or other students will answer you.

About Hexlet learning process

For full access to the course you need a professional subscription.

A professional subscription will give you full access to all Hexlet courses, projects and lifetime access to the theory of lessons learned. You can cancel your subscription at any time.

Get access
130
courses
1000
exercises
2000+
hours of theory
3200
tests

Sign up

Programming courses for beginners and experienced developers. Start training for free

  • 130 courses, 2000+ hours of theory
  • 1000 practical tasks in a browser
  • 360 000 students
By sending this form, you agree to our Personal Policy and Service Conditions

Our graduates work in companies:

<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.bookmate">Bookmate</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.healthsamurai">Healthsamurai</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.dualboot">Dualboot</span>
<span class="translation_missing" title="translation missing: en.web.courses.lessons.registration.abbyy">Abbyy</span>
Suggested learning programs

From a novice to a developer. Get a job or your money back!

Frontend Developer icon
Profession
beginner
Development of front-end components for web applications
start anytime 10 months

Use Hexlet to the fullest extent!

  • Ask questions about the lesson
  • Test your knowledge in quizzes
  • Practice in your browser
  • Track your progress

Sign up or sign in

By sending this form, you agree to our Personal Policy and Service Conditions
Toto Image

Ask questions if you want to discuss a theory or an exercise. Hexlet Support Team and experienced community members can help find answers and solve a problem.