Владислав Калачев

Учим useState на примерах — React Hooks

5 865

Доброго времени суток, друзья. На дворе 2К20 год и начиная с версии React 16.8 для работы с состояниями в функциональных компонентах появились хуки. В этой статье мы подробно разберем один из хуков — useState.

Для чего вообще предназначен useState? Если вы работаете с React, то должны были сталкиваться с понятием состояния. Раньше, с состоянием в React можно было работать только в класс компонентах, используя метод setState, которой мы получали наследуя рабочий класс от класса React.Component. Долгое время компоненты функции использовались в качестве ‘глупых’ компонентов, не имеющих состояния, и были предназначены только для рендеринга элементов ui. С версии React 16.8 данный функционал был доработан, теперь при работе с состояниями в функциональных компонентах можно использовать хуки.

Видео на эту тему.

Правила хуков

Сперва мы рассмотрим основные правила применения хуков и их особенности:

1. Используйте хуки только на верхнем уровне. Запрещено вызывать хук в методе или цикле. Следуя этому правилу, можно гарантировать правильность выполнения неизменной последовательности при рендере компонента.

2. React полагается на порядок вызова хуков, что позволяет использовать хуки useState, useEffect в одном компоненте многократно.

Пример:


function Example() {

  const [name, setName] = useState('Name')
  const [surname, setSurname] = useState('Surname')
      
    return (
        <div>
            Example
        </div>
}

3. Не применяйте условия, вызывая хуки, поскольку текущая последовательность вызовов в компоненте может быть нарушена. Допустимо использовать выражение внутри хука.

Пример:


function Example() {

        // ошибка
	if(true) {
		useState()
	}
       
    useState()
    useState()
    
    return (
        <div>
            Example
        </div>
    )
}

Практика использования useState

Давайте на примере подробно разберём работу с useState. Для начала работы следует импортировать метод из React.

Пример:


import React, {useState} from 'react'

Далее вызовем его в функции (соблюдая все правила, которые мы рассмотрели выше).

Пример:


import React, {useState} from 'react'

function Count() {

   const [count, setCount] = useState(1)

    return (
        <div>
	    Count
        </div>
    )
}

Хук useState() принимает один параметр в виде начального состояния, которое может быть представлено как значение (строка, число, массив, объект и т.д.) или функция. После вызова функции, useState возвращает массив из двух элементов. Первым элементом является начальное состояние, которое мы передаём,  вторым — функция, изменяющая это состояние при ее вызове и передаче нового значения. Существует несколько способов получения элементов массива:

1. Через индекс

Пример: 


import React, {useState} from 'react'

function Count() {

     const arr = useState(1)

     const increment = () => {
         arr[1](arr[0]+ 1)
     }

    return (
        <div>
	      <button onClick={increment}>+</button>
              <span>
		  {arr[0]}
	     </span>
        </div>
    )
}

2. Через современный синтаксис деструктуризации  ES6 

Пример: 


import React, {useState} from 'react'

function Count() {

    const [count, setCount] = useState(1)

    const increment = () => {
      setCount(count + 1)
    }

    return (
       <div>
	    <button onClick={increment}>+</button>
            <span>
		{count}
	    </span>
       </div>
    )
}

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

Разберем подробнее, что происходит в функции Count. Мы вызвали хук useState, в параметр которого передали значение 1. Это значение будет является начальным состоянием.  Ниже добавлена функция increment, которая будет изменять значение по клику на кнопку и добавлять +1 к текущему состоянию, вызывая setCount(count + 1). Таким образом, каждый клик будет инкрементировать новое значение и счётчик будет увеличиваться.

Рассмотрим кейс, когда нужно использовать несколько вызовов функций useState подряд.


import React, {useState} from 'react'

function Count() {

    const [count, setCount] = useState(1)

    const increment = () => {
	setCount(count + 1)
	setCount(count + 1)
    }

    return (
       <div>
	   <button onClick={increment}>+</button>
           <span>
		{count}
	    </span>
       </div>
    )
}

По клику на кнопку мы обнаружим, что значение увеличивается только на +1, хотя предполагалось, что счетчик покажет +2. Для решения этой задачи в функции изменения setCount можно вызвать функцию в первом аргументе, которой будет возвращено предыдущее состояние count. При каждом новом вызове setCount, значение count будет меняться.

Пример:


import React, {useState} from 'react'

function Count() {

    const [count, setCount] = useState(1)
    const increment = () => {
          setCount(prevCount =>  prevCount + 1)
          setCount(prevCount =>  prevCount + 1)
    }
 
    return (
       <div>
	    <button onClick={increment}>+</button>
           <span>
		 {count}
	    </span>
       </div>
    )
}

Теперь каждый раз по клику, мы получим нужный результат +2.

Работа с объектами

Часто требуется использовать большое количество состояний в одном компоненте. Рассмотрим кейс с формой, содержащей несколько полей.

Пример:


import React, {useState} from 'react'

function LoginForm() {
  const [name, setName] = useState('');
  const [password, setPassword] = useState('');

  const submit = (e) => {
    e.preventDefault();
    console.log(name, password);
  };

  return (
    <form onSubmit={submit}>
      <label>
        Имя:
        <input
          value={name}
          onChange={event => setName(event.target.value)}
          name="name"
          type="text"
        />
      </label>
        <label>
        Пароль:
        <input
          value={password}
          onChange={event => setPassword(event.target.value)}
          name="password"
          type="password"
        />
      </label>
          <button>Отправить</button>
    </form>
  );
}

Чтобы не писать useState множество раз, можно использовать объект (или массив) для хранения всего состояния целиком. На примере объекта, посмотрим как можно работать с формой.

Пример: 


function LoginForm() {

  const [form, setForm] = useState({
    name: '',
    password: ''
  });

  const submit = e => {
    e.preventDefault();
    console.log(form.username, form.password);
  };

  const update = e => {
    setState({
      ...form,
      [e.target.name]: e.target.value
    });
  };

  return (
    <form onSubmit={submit}>

      <label>
     	Имя:
        <input
          value={form.name}
          name="name"
          onChange={update}
        />
      </label>
      
    <label>
        Пароль:
        <input
          value={form.password}
          name="password"
          type="password"
          onChange={update}
        />
      </label>

      <button>Отправить</button>
    </form>
  );
}

В useState помещаем начальное состояние формы. При отправлении формы вызываем метод submit, в котором увидим текущее состояние. Для изменения состояний input’ов используется метод update, в котором через атрибут name и value из e.target получаем данные и при изменении отправляем их как новое состояние формы через setState. Чтобы каждый раз не ‘перетирать’ предыдущее состояние другого input’a, используется спред оператор (…), позволяющий копировать предыдущее состояние.

Заключение

Сегодня мы рассмотрели особенности работы с хуком useState, а также общие правила использования хуков. На примерах разобрали некоторые рабочие кейсы (состояния, объекты, работа с формой).  Надеюсь, что данная статья была вам полезна. Учитесь, думайте, пишите код. Удачного кодинга, друзья!

Полезные материалы:

https://ru.reactjs.org/docs/hooks-intro.html — официальная документация по React Hooks

Подписывайтесь на наш канал в Telegram и на YouTube для получения самой последней и актуальной информации. 

Комментарии:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *