Одним из наиболее используемых встроенных хуков является useState, который позволяет определить состояние компонента. Например, определим простейший компонент, который применяет этот хук:
<!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"> function Counter() { const [count, setCount] = React.useState(0); return ( <div> <h3>Count = {count}</h3> <button onClick={() => setCount(count + 1)}> Увеличить </button> </div> ); } ReactDOM.createRoot( document.getElementById("app") ) .render( <Counter /> ) </script> </body> </html>
Итак, здесь определен компонент Counter
, причем определен в виде функции, так как мы не можем использовать хуки в классах-компонентах.
Вначале определяем переменные состояния:
const [count, setCount] = React.useState(0);
В данном случае определяются две переменных: count
и setCount
. Переменная count
собственно
хранит состояние - некоторый объект, а переменная setCount
представляет функцию, которая позволяет изменять значение переменной count.
Несмотря на то, что между названиями переменных есть связь - count и setCount, однако это не более чем условность. Названия переменных могут быть вообще никак между собой не связаны.
Что за значение будет хранить переменная count
? А хранит она то значение, которое передается в функцию useState()
-
в данном случае это число 0.
Чтобы инспектировать значение переменной count, ее значение выводится в заголовке h3:
<h3>Count = {count}</h3>
В коде компонента определена кнопка, которая по нажатию будет вызывать изменение значения переменной count
onClick={() => setCount(count + 1)}
В данном случае применяется стрелочная функция, которая выполняет выражение setCount(count + 1)
, которое увеличивает
значение count на единицу.
В итоге по нажатию на кнопку изменится значение переменной count:
Итак, хук useState
определяет переменные состояния. Единственный аргумент хука определяет начальное значение переменной состояния.
А возвращает useState()
массив из двух объектов. Первый объект представляет значение состояния, а второй объект представляет функцию,
которая обновляет это значение. Например, выше состояние определялось следующим образом:
const [count, setCount] = React.useState(0);
В данном случае для определения переменных применяется синтаксис декомпозиции или destructuring. Но также мы могли бы использовать и стандартный синтаксис массивов для получения значений переменных:
var countStateArray= React.useState(0); var count = countStateArray[0]; var setCount = countStateArray[1];
При необходимости можно определять множество переменных состояния. Например:
<!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"> function User() { const [name, setName] = React.useState("Tom"); const [age, setAge] = React.useState(36); return ( <div> <h3>Имя: {name}</h3> <h3>Возраст: {age}</h3> </div> ); } ReactDOM.createRoot( document.getElementById("app") ) .render( <User /> ); </script> </body> </html>
В данном случае определены две переменных состояния: name и age.
Также мы можем изменять значения этих переменных. Например, добавим к предыдущему примеру изменение переменных:
<!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"> function User() { const [name, setName] = React.useState("Tom"); const [age, setAge] = React.useState(36); function handleNameChange(event) { setName(event.target.value); } function handleAgeChange(event) { setAge(event.target.value); } return ( <div> <h3>Имя: {name}</h3> <h3>Возраст: {age}</h3> <div> <p>Имя: <input type="text" value={name} onChange={handleNameChange} /></p> <p>Возраст: <input type="number" min="0" max="110" value={age} onChange={handleAgeChange} /></p> </div> </div> ); } ReactDOM.createRoot( document.getElementById("app") ) .render( <User /> ); </script> </body> </html>
В данном случае для обновления значений переменных name и age в компоненте определены два поля ввода. Событие onChange
каждого поля привязано к одной из функций компонента - handleAgeChange или handleNameChange. Поэтому изменение значения в каждом поле вызовет
соответствующую функцию. Например, при вводе текста для имени пользователя будет вызываться функция handleNameChange()
:
function handleNameChange(event) { setName(event.target.value); }
По умолчанию в функцию обработки события поля ввода input передается информация о событии, из которой с помощью свойства target.value
мы можем получить
введенное значение и затем передать его в функцию изменения состояния: setName(event.target.value)
Однако нам необязательно определять отдельные переменные для хранения состояния. Хук useState
равным образом позволяет определять массивы и
комплексные объекты. Например, перепишем предыдущий пример, объединив переменные в один объект:
<!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"> function UserData() { const [user, setUser] = React.useState({name:"Tom", age: 36}); function handleNameChange(event) { setUser({name: event.target.value, age: user.age}); } function handleAgeChange(event) { setUser({name: user.name, age: event.target.value}); } return ( <div> <h3>Имя: {user.name}</h3> <h3>Возраст: {user.age}</h3> <div> <p>Имя: <input type="text" value={user.name} onChange={handleNameChange} /></p> <p>Возраст: <input type="number" min="0" max="110" value={user.age} onChange={handleAgeChange} /></p> </div> </div> ); } ReactDOM.createRoot( document.getElementById("app") ) .render( <UserData /> ) </script> </body> </html>
Здесь состояние компонента представлено сложным объектом, который имеет два свойства: name и age. Но в данном случае при обновлении следует учитывать, что функция обновления (в данном случае - setUser) полностью изменяет хранимое значение состояния, поэтому нам надо полностью переопределить его свойства:
setUser({name: event.target.value, age: user.age});
В итоге мы получим тот же результат:
Однако если у нас объект имеет множество свойств, то перечисление всех свойств и их значений может быть утомительным. И в этом случае мы можем сократить запись с помощью spread-оператора ...:
<!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"> function UserData() { const [user, setUser] = React.useState({name:"Tom", age: 36}); function handleNameChange(event) { setUser({...user, name: event.target.value}); } function handleAgeChange(event) { setUser({...user, age: event.target.value}); } return ( <div> <h3>Имя: {user.name}</h3> <h3>Возраст: {user.age}</h3> <div> <p>Имя: <input type="text" value={user.name} onChange={handleNameChange} /></p> <p>Возраст: <input type="number" min="0" max="110" value={user.age} onChange={handleAgeChange} /></p> </div> </div> ); } ReactDOM.createRoot( document.getElementById("app") ) .render( <UserData /> ); </script> </body> </html>