Використання хука стану

Хуки — це новинка в React 16.8. Вони дозволяють вам використовувати стан та інші можливості React без написання класу.

На початковій сторінці ми ознайомились з хуками на цьому прикладі:

import React, { useState } from 'react';

function Example() {
  // Оголошуємо нову змінну стану, яку назвемо "count"
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Ви натиснули {count} разів</p>
      <button onClick={() => setCount(count + 1)}>
        Натисни мене
      </button>
    </div>
  );
}

Ми розпочнемо вивчення хуків, порівнюючи цей код з еквівалентним кодом на основі класу.

Еквівалентний приклад з класом

Якщо ви вже використовували класи в React, цей код має бути знайомим:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  render() {
    return (
      <div>
        <p>Ви натиснули {this.state.count} разів</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Натисни мене
        </button>
      </div>
    );
  }
}

Стан ініціалізується як { count: 0 } і ми інкрементуємо state.count, коли користувач натискає на кнопку, викликаючи this.setState(). Ми будемо використовувати фрагменти коду цього класу на цій сторінці.

Примітка

Ви можете поцікавитись, чому ми використовуємо звичайний лічильник замість більш реального прикладу. Це зроблено для того, щоб ви могли сконцентруватись на API під час ваших перших кроків з хуками.

Хуки та функціональні компоненти

Нагадаємо, що функціональні компоненти в React виглядають так:

const Example = (props) => {
  // Тут можна використовувати хуки!
  return <div />;
}

або так:

function Example(props) {
  // Тут можна використовувати хуки!
  return <div />;
}

Раніше ви могли знати, що такі компоненти відомі як “компоненти без стану”. Зараз ми покажемо, як додавати до них стан, а тому надалі ми будемо називати їх “функціональні компоненти”.

Хуки не працюють всередині класів. Але ви можете використовувати їх замість написання класів.

Що таке хук?

Наш наступний приклад починається імпортом хука useState із React:

import React, { useState } from 'react';

function Example() {
  // ...
}

Що таке хук? Хук — це спеціальна функція, що дозволяє вам “зачепитись” за можливості React. Наприклад, хук useState дозволяє вам додавати стан до функціональних компонентів. Ми вивчимо інші хуки дещо пізніше.

Коли я маю використовувати хук? Якщо ви пишете функціональний компонент і розумієте, що вам потрібно додати деякий стан до нього, раніше ви мали перетворювати його у клас. Зараз ви можете використати хук усередині функціонального компонента. Саме це ми зробимо зараз!

Примітка:

Є кілька особливих правил про те, коли ви можете застосовувати хуки всередині компонента, а коли ні. Ми дізнаємось про них у розділі Правила хуків.

Оголошення змінної стану

У класі ми ініціалізували стан count зі значенням 0, встановивши this.state рівним { count: 0 } у конструкторі:

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

У функціональному компоненті ми не маємо this, а тому не можемо присвоювати чи зчитувати this.state. Натомість, ми викличемо хук useState у нашому компоненті напряму:

import React, { useState } from 'react';

function Example() {
  // Оголошуємо нову змінну стану, яку назвемо "count"
  const [count, setCount] = useState(0);

Що робить виклик useState? Він оголошує “змінну стану”. Наша змінна зветься count, але ми могли назвати її як завгодно, наприклад banana. Таким чином, ми “зберігаємо” деякі значення між викликами функції. useState — це новий спосіб використання тих можливостей, що їх this.state надає у класі. Зазвичай, змінні “зникають” після виходу з функції, але React збереже змінні стану.

Які аргументи ми передаємо в useState? Єдиним аргументом хука useState() є початковий стан. На відміну від класів, стан не повинен бути об’єктом. Ми можемо зберігати число чи рядок, якщо це все, що нам потрібно. У нашому прикладі ми рахуємо кількість кліків користувача, а отже, передаємо 0 як початковий стан нашої змінної. (Якщо нам потрібно зберегти два різних значення у стані, ми маємо викликати useState() двічі.)

Що повертає useState? Він повертає пару значень: поточний стан і функцію, яка його оновлює. Ось чому ми написали const [count, setCount] = useState(). Це нагадує this.state.count та this.setState у класі, але ви отримуєте їх у парі. Якщо ви не знайомі з синтаксисом, який ми використали, ми повернемось до нього в кінці цієї сторінки.

Тепер ми знаємо, що робить хук useState, а тому наш приклад повинен бути більш зрозумілим:

import React, { useState } from 'react';

function Example() {
  // Оголошуємо нову змінну стану, яку назвемо "count"
  const [count, setCount] = useState(0);

Ми оголошуємо змінну count і встановлюємо їй значення 0. React запам’ятає її поточне значення між повторними рендерами і надасть актуальне значення у нашу функцію. Якщо нам потрібно оновити поточний count, ми можемо викликати setCount.

Примітка

Можливо ви запитаєте: чому useState назвали, а не createState?

“Create” (створити) є не зовсім точним, оскільки стан створюється тільки під час першого рендеру нашого компонента. Під час наступних рендерів useState повертає поточний стан. Інакше це був би не “стан” взагалі! Також є причина тому, що імена хуків завжди починаються з use. Про неї ми дізнаємось у розділі Правила хуків.

Зчитування стану

Коли ми хочемо відобразити поточне значення лічильника у класі, ми зчитуємо значення this.state.count:

  <p>Ви натиснули {this.state.count} разів</p>

У функції ми можемо напряму використовувати count:

  <p>Ви натиснули {count} разів</p>

Оновлення стану

У класі ми маємо викликати this.setState() для оновлення стану count:

  <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    Натисни мене
  </button>

У функції ми вже маємо setCount і count у ролі змінних, а тому this нам не потрібен:

  <button onClick={() => setCount(count + 1)}>
    Натисни мене
  </button>

Підсумок

Давайте крок за кроком повторимо те, що ми вивчили і перевіримо наші знання.

 1:  import React, { useState } from 'react';
 2:
 3:  function Example() {
 4:    const [count, setCount] = useState(0);
 5:
 6:    return (
 7:      <div>
 8:        <p>Ви натиснули {count} разів</p>
 9:        <button onClick={() => setCount(count + 1)}>
10:         Натисни мене
11:        </button>
12:      </div>
13:    );
14:  }
  • Рядок 1: Ми імпортуємо хук useState із React. Це дозволить нам зберігати локальний стан у функціональному компоненті.
  • Рядок 4: Усередині компоненту Example, викликавши хук useState, ми оголошуємо нову змінну стану. Хук повертає пару значень, яким ми даємо ім’я. Ми називаємо нашу змінну count, тому що вона зберігає кількість кліків на кнопку. Ми ініціалізуємо її нулем, передавши 0, як єдиний аргумент useState. Друге значення, повернуте хуком, є функцією. Оскільки вона дозволяє нам оновлювати count, ми назвемо її setCount.
  • Рядок 9: Коли користувач натискає кнопку, ми викликаємо setCount із новим значенням. React повторно відрендерить компонент Example, передавши йому оновлене значення count.

Спочатку все це може здатись надто складним. Не поспішайте! Якщо ви заплутались з поясненням, перегляньте код ще раз і спробуйте прочитати його з початку до кінця. Ми обіцяємо, що коли ви спробуєте “забути”, як стан працює в класах, і поглянете на цей код свіжим поглядом, то все відразу стане на свої місця.

Порада: Що означають квадратні дужки?

Ви могли звернути увагу на квадратні дужки під час оголошення змінної стану:

  const [count, setCount] = useState(0);

Імена зліва не є частиною React API. Ви вільні обирати імена ваших власних змінних стану:

  const [fruit, setFruit] = useState('банан');

Такий синтаксис JavaScript називається “деструктуризація масивів”. Це означає, що ми створюємо дві нові змінні — fruit та setFruit, де fruit зберігає перше значення, повернуте useState, а setFruit — друге. Таке присвоєння рівнозначне коду:

  var fruitStateVariable = useState('банан'); // Повертає пару
  var fruit = fruitStateVariable[0]; // Перший елемент пари
  var setFruit = fruitStateVariable[1]; // Другий елемент пари

Після оголошення змінної стану за допомогою виклику useState, ми отримуємо пару — масив із двох елементів. Перший елемент є поточним значенням, а другий — функцією, що дозволяє оновити його. Використання [0] і [1] для доступу до них може заплутати, тому що індекси не мають особливого значення. Саме тому ми використовуємо деструктуризацію масиву.

Примітка

Ви можете поцікавитись, звідки React знає про те, якому компоненту відповідає useState, оскільки ми не передаємо йому нічого подібного до this. Ми дамо відповідь на це та багато інших питань у розділі FAQ.

Порада: Використання кількох змінних стану

Оголошення змінних стану у вигляді пари [something, setSomething] також є корисним у тому сенсі, що воно дає нам можливість давати різні імена різним змінним стану, якщо нам потрібно використати їх декілька:

function ExampleWithManyStates() {
  // Оголошуємо кілька змінних стану!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);

У компоненті вище ми маємо age, fruit і todos у якості локальних змінних і можемо оновлювати кожну з них окремо:

  function handleOrangeClick() {
    // Схоже на this.setState({ fruit: 'апельсин' })
    setFruit('апельсин');
  }

Ви не зобов’язані використовувати багато змінних стану. Змінні стану можуть так само зберігати об’єкти і масиви, а отже, ви й досі можете групувати пов’язані дані. Зверніть увагу, що на відміну від this.setState у класі, оновлення змінної стану завжди заміняє її замість злиття.

Ви можете знайти більше рекомендацій про розділення незалежних змінних стану у FAQ.

Наступні кроки

На цій сторінці ми дізнались про один з хуків, які нам надає React, із назвою useState. Іноді ми будемо посилатись на нього як на “хук стану”. Він дозволяє нам додавати локальний стан до функціональних компонентів, що ми і зробили вперше!

Також ми дізнались більше про хуки в цілому. Хуки — це функції, що дозволять вам “зачепитись” за можливості React у функціональних компонентах. Їхні імена завжди починаються з use. Також існує ще кілька хуків, про які ми поки що не знаємо.

Настав час перейти до вивчення наступного хука, а саме — useEffect. Він дозволяє виконувати побічні ефекти в компонентах і нагадує методи життєвого циклу в класах.