Состояние. Управление компонентами-классами

State

Последнее обновление: 01.04.2022

Объект state описывает внутреннее состояние компонента, он похож на props за тем исключением, что состояние определяется внутри компонента и доступно только из компонента.

Если props представляет входные данные, которые передаются в компонент извне, то состояние хранит такие объекты, которые создаются в компоненте и полностью зависят от компонента.

Также в отличие от props значения в state можно изменять.

И еще важный момент - значения из state должны использоваться при рендеринге. Если какой-то объект не используется в рендерниге компонента, то нет смысла сохранять его в state.

Нередко state описывает какие-то визуальные свойства элемента, которые могут изменяться при взаимодействие с пользователем. Например, кнопку нажали, и соответственно можно изменить ее состояние - придать ей какой-то другой цвет, тень и так далее. Кнопку нажали повторно - можно вернуть исходное состояние.

Стоит отметить, что традиционно объект state применялся только в классах-компонентах. В функциональных же компонентах для управления состоянием применяется другая архитектура, основанная на хуках.

При использовании класса-компонента единственное место, где можно установить объект state - это конструктор класса:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Hello React</title>
</head>
<body>
    <div id="app"></div>
     
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class Hello extends React.Component {
            constructor(props) {
                super(props);
                this.state = {welcome: "Добро пожаловать на сайт!"};
            }
            render() {
                return <h1>{this.state.welcome}</h1>;
            }
        }
        ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
            <Hello />
        );
    </script>
</body>
</html>

При определении конструктора компонента в нем должен вызываться конструктор базового класса, в который передается объект props.

Состояние State в React

Обновление состояния

Для обновления состояния вызывается функция setState():

this.setState({welcome: "Привет React"});

Изменение состояния вызовет повторный рендеринг компонента, в соответствии с чем веб-страница будет обновлена.

В то же время не стоит изменять свойства состояния напрямую, например:

this.state.welcome = "Привет React";

В данном случае изменения повторного рендеринга компонента происходить не будет.

При этом нам не обязательно обновлять все его значения. В процессе работы программы мы можем обновить только некоторые свойства. Тогда необновленные свойства будут сохранять старые значения.

Пример обновления состояния:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>Hello React</title>
    <style>
        button{
            width: 100px;
            height:30px;
            border-radius: 4px;
            margin:50px;
        }
        .on{
            color:#666;
            background-color: #ccc;
        }
        .off{
            color:#888;
            background-color: white;
        }
    </style>
</head>
<body>
    <div id="app"></div>
     
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class ClickButton extends React.Component {
            
           constructor(props) {
               super(props);
               this.state = {class: "off", label: "Нажми"};
                
               this.press = this.press.bind(this);
           }
           press(){
               let className = (this.state.class==="off")?"on":"off";
               this.setState({class: className});
           }
           render() {
               return <button onClick={this.press} className={this.state.class}>{this.state.label}</button>;
           }
       }
        ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
            <ClickButton />
        );
    </script>
</body>
</html>

Здесь определен компонент ClickButton, который по сути представляет кнопку. В состоянии кнопки хранится два свойства - надпись и класс. При нажатии на кнопку мы будем переключать с одного класса на другой. Событие нажатия кнопки через атрибут onClick связано с методом press(), в котором переключается класс кнопки.

При этом свойство state.label остается неизменным.

Обновление состояния setState и события в React

Асинхронное обновление

При наличии нескольких вызовов setState() React может объединять их в один общий пакет обновлений для увеличения производительности.

Так как объекты this.props и this.state могут обновляться асинхронно, не стоит полагаться на значения этих объектов для вычисления состояния. Например:

this.setState({
  counter: this.state.counter + this.props.increment,
});

Для обновления надо использовать другую версию функции setState(), которая в качестве параметра принимает функцию. Данная функция имеет два параметра: предыдущее состояние объекта state и объект props на момент применения обновления:

this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});

Например, определим два последовательных вызова setState():

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>
      
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class ClickButton extends React.Component {
             
           constructor(props) {
               super(props);
               this.state = {counter: 0};
               this.press = this.press.bind(this);
           }
           press(){
			   this.setState({counter: this.state.counter + parseInt(this.props.increment)});
			   this.setState({counter: this.state.counter + parseInt(this.props.increment)});
           }
           render() {
               return <div>
							<button onClick={this.press}>Count</button>
							<div>Counter: {this.state.counter} <br />Increment: {this.props.increment}</div>
						</div>
           }
       }
        ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
           <ClickButton increment="1" />
        );
    </script>
</body>
</html>

В props определено свойство increment - значение, на которое будет увеличиваться свойство counter в state (this.setState({counter: this.state.counter + parseInt(this.props.increment)});). При чем при нажатии кнопки мы предполагаем, что функция setState() будет вызываться два раза, соответственно значение state.counter при нажатии кнопки должно увеличиваться на 2. Однако в реальности увеличение происходит лишь на 1:

Синхронное обновление state in React

Теперь изменим код, применив второй вариант функции setState():

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>METANIT.COM</title>
</head>
<body>
    <div id="app"></div>
      
    <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <script type="text/babel">
        class ClickButton extends React.Component {
             
           constructor(props) {
               super(props);
               this.state = {counter: 0};
                 
               this.press = this.press.bind(this);
           }
		   incrementCounter(prevState, props) {
				  return {
					counter: prevState.counter + parseInt(props.increment)
				  };
			}
           press(){
			   this.setState(this.incrementCounter);
			   this.setState(this.incrementCounter);
           }
           render() {
               return <div>
						<button onClick={this.press}>Count</button>
						<div>Counter: {this.state.counter}<br /> Increment: {this.props.increment}</div>
				</div>
           }
       }
         
       ReactDOM.createRoot(
            document.getElementById("app")
        )
        .render(
           <ClickButton increment="1" />
       );
    </script>
</body>
</html>

Чтобы избежать повторения, все действия по инкременту вынесены в отдельную функцию - incrementCounter, однако опять же функция setState() вызывается два раза. И теперь инкремент будет срабатывать два раза при однократном нажатии, собственно как и определено в коде и как и должно быть:

Ассинхронное обновление state in React
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850