React Tutorial, Part 17: The Fifth Stage of Working on a TODO Application, Modifying the State of Components

Original author: Bob Ziroll
  • Transfer
  • Tutorial
In today's part of the translation of the React course, we suggest you complete the next practical task and present to your attention a story on how to modify the state of React components. → Part 1: course overview, reasons for the popularity of React, ReactDOM and JSXPart 2: functional componentsPart 3: component files, project structurePart 4: parent and child componentsPart 5: start of work on a TODO application, basics of stylingPart 6: about some features of the course, JSX and JavaScriptPart 7: built-in stylesPart 8: continued work on the TODO application, familiarity with the properties of components

image









Part 9: component properties
Part 10: workshop on working with component properties and stylization
Part 11: dynamic markup formation and map array method
Part 12: workshop, third stage of work on a TODO application
Part 13: components based on classes
Part 14: workshop on class-based components, component status
Part 15: workshops on working with components
Part 16: the fourth stage of work on a TODO application, event handling
Part 17: fifth stage of work on a TODO application , modification with component status
Part 18: the sixth stage of work on the TODO application
Part 19: the methods of the component life cycle
Part 20: the first lesson on conditional rendering
Part 21: the second lesson and the workshop on conditional rendering
Part 22: the seventh stage of work on the TODO application, loading data from external sources
Part 23: the first lesson on working with forms
Part 24: the second lesson on working with forms
Part 25: a workshop on working with forms
Part 26: application architecture, the Container / Component pattern
Part 27: course project

Lesson 31. Workshop. TODO application. Stage number 5


Original

▍Job


When launching our Todo application, you may notice that a notification is displayed in the console that indicates that we, having configured the property of an checkedelement in the component TodoItem, have not provided a mechanism for interacting with this element in the form of an event handler onChange. When working with the application interface, this results in the fact that the flags displayed on the page cannot be checked or unchecked.

Here you are invited to equip the type of element checkboxcomponent TodoItemevent handler onChange, which, at this stage of the work, to present enough in the form of a function that prints something to the console.

▍Solution


Here's what the component code TodoItemthat is stored in the file looks like now TodoItem.js:

import React from "react"
function TodoItem(props) {
    return (
        
                       

{props.item.text}

       
   ) } export default TodoItem

This is what the console displays when the application starts.


Notification in the console.

At the same time, the flags do not respond to our effects.

In order to get rid of this notification and prepare the project for further work, it is enough to assign an event handler to the onChangeelement checkbox. Here's what it looks like in the code:

import React from "react"
function TodoItem(props) {
    return (
        
            console.log("Changed!")}            />            

{props.item.text}

       
   ) } export default TodoItem

Here we, as a handler, use a simple function that prints a word to the console Checked!. At the same time, clicking on the flags does not lead to a change in their state, but the notification from the console, as can be seen in the following figure, disappears.


The flags still do not work, but the notification from the console has disappeared.

This small change made to the application will allow us, after we deal with the change in the state of the components, to make the flags work correctly.

Lesson 32. Changing the State of Components


Original

Let's start with a standard application created with the help of create-react-appwhich file App.jscontains the following code:

import React from "react"
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }
    render() {
        return (
            
               

{this.state.count}

                           
       )    } } export default App

The stylesheet index.cssthat is index.jsincluded in the file contains the following stylesheet:

div {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
h1 {
    font-size: 3em;
}
button {
    border: 1px solid lightgray;
    background-color: transparent;
    padding: 10px;
    border-radius: 4px;   
}
button:hover {
    cursor: pointer;
}
button:focus {
    outline:0;
}

At this stage, the application looks like the one shown in the following figure.


Application page in a browser

Today we will talk about how to change the state of components. If the component has a state, this allows, by initializing it, to store some data in it. But if the state could not be changed, then the component would not have much benefit from its presence, storing the data in it would not be very different from, for example, hard-copying them in the component code.

Let's talk about the application, on the example of which we will consider working with the state of the component. The component Appwhose code is presented above is a class-based component. This is quite obvious, since we need this component to have a state. In the component code, we use the constructor.

In it, we, as always, call the methodsuper()and initialize the state by writing a property into it countand assigning it an initial value 0. In the method, render()we display a first-level header representing the value of the property countfrom the state of the component, as well as a button with the word Change!. All of this is formatted using styles.

If, at this stage of work on the application, open it in a browser and click on the button, then, of course, nothing will happen. But we need to click on the button to change the state of the component, affecting its property count. At the same time, we have already studied the event processing methodology in React, and our task is to create a mechanism that, responding to a click on a button, changes the state property count.

Let's get down to solving our problem by equipping the button with an event handler onClick, which, for starters, will simply output something to the console.

To do this, we will add a new method to the component class. You can call it anything you like, but it is customary to call such methods so that their names would indicate the events they are processing. As a result, we, since we are going to handle the event with its help click, will call it handleClick(). This is how the component code will now look App.

import React from "react"
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }
    handleClick() {
        console.log("I'm working!")
    }
    render() {
        return (
            
               

{this.state.count}

                           
       )    } } export default App

Please note that referring to this method from render(), we use the view construct this.handleClick.

Now, if you click on the button, the corresponding message will appear in the console.


Clicking on the button calls the class method.

Now let's make the clicking on the button increase the number displayed above it, that is, modify the state of the component. Maybe try changing the state of a component directly, in a method handleClick()? Let's say that if we rewrite this method like this:

handleClick() {
    this.state.count++
}

I must say right away that this does not work with the state of components in React. Attempting to execute such code will throw an error.

The condition of the component can be compared with the clothes that a person wears. If he wants to change clothes, he does not alter or repaint the clothes without taking off himself, but takes off her and puts on something else. As a matter of fact, this is exactly how they work with the state of components.

You may remember that we talked about a special method used to modify state, available in components based on classes due to the fact that they extend the class React.Component. This is the method setState(). It is used in cases where you need to change the state of a component. This method can be used in different ways.

Recall that a state is an object. Let's try to pass to the method setState()an object that will replace the state. We rewrite the method handleClick()like this:

handleClick() {
    this.setState({ count: 1 })
}

Attempting to use such a method will cause a mistake TypeError: Cannot read property 'setState' of undefined. In fact, what we are talking about now is causing a lot of controversy among the React developers, and now I am going to show you a very simple way to solve this problem, which, at first glance, may seem unusual.

The point is that each time, creating a class method ( handleClick()in our case), in which it is planned to use the method setState(), this method must be associated with this. This is done in the constructor. The component code after this modification will look like this:

import React from "react"
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
        this.handleClick = this.handleClick.bind(this)
    }
    handleClick() {
        this.setState({ count: 1 })
    }
    render() {
        return (
            
               

{this.state.count}

                           
       )    } } export default App

Now, after clicking on the button, the Change!number 1 will appear above it, error messages will not be displayed.


Pressing the button modifies the state.

True, the button turned out to be “one-time”. After the first click on it, it 0changes to 1, and if you click on it again, nothing will happen. In general, this is not surprising. The code that is called when the button is clicked does its job, each time changing the state to a new one, however, after the first click on the button, the new state in which the countnumber is stored in the property 1will not differ from the old one. In order to solve this problem, consider another way of working with the method setState().

If we are not interested in what the previous state of the component was, then we can simply pass an object to this method, which will replace the state. But it often happens that the new state of a component depends on the old. In our case, this means that, based on the value of the property countthat is stored in the previous version of the state, we want to add to this value 1. In cases where to change the state you need to be aware of what was stored in it earlier, setState()you can pass a function to the method , which, as a parameter, receives the previous version of the state. You can name this parameter as you like, in our case it will be prevState. The procurement of this function will look like this:

handleClick() {
    this.setState(prevState => {
    })
}

You might think that in such a function it’s enough to simply refer to the state using the view construct this.state, but this approach will not suit us. Therefore, it is important that this function accepts the previous version of the component state.

The function should return a new version of the state. Here is the method handleClick()that solves this problem:

handleClick() {
    this.setState(prevState => {
        return {
            count: prevState.count + 1
        }
    })
}

Note that to use the new property value, countwe use the construct count: prevState.count + 1. You might think that the view design is also suitable count: prevState.count++, but the operator ++leads to a modification of the variable to which it is applied, this will mean an attempt to modify the previous version of the state, so we do not use it here.

The full code of the component file at this stage will look like this:

import React from "react"
class App extends React.Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
        this.handleClick = this.handleClick.bind(this)
    }
    handleClick() {
        this.setState(prevState => {
            return {
                count: prevState.count + 1
            }
        })
    }
    render() {
        return (
            
               

{this.state.count}

                           
       )    } } export default App

Now each click on the button increases the counter value.


Each click on the button increases the counter value.

What we just figured out opens up great opportunities for us in the development of React applications.

We said earlier that a parent component can, through a property mechanism, pass properties from its own state to child components. If React detects a change in state of the parent component, it will re-render the child component to which this state is passed. It looks like a method call render(). As a result, the child component will reflect the new data stored in the state of the parent component.

Summary


Today you have prepared the Todo application for further work on it, and also familiarized yourself with the mechanisms used in React to change the state of a component. Next time you will be asked to expand the capabilities of the training application using what you learned today.

Dear readers! How do you feel about the fact that the state of components in React cannot be changed directly without using special mechanisms?


Also popular now: