Введение в стейты в React


Следующая концепция, которую мы с вами разберем, называется стейты. Стейты представляют собой реактивные переменные компонентов.

Реактивность означает, что при изменении стейта изменения произойдут во всех местах, где этот стейт используется. Технически это достигается путем перендерования всего компонента при изменении какого-либо стейта.

Для использования стейтов для начала необходимо импортировать функцию useState:

import React, { useState } from 'react';
Функция useState параметром принимает начальное значение стейта, а своим результатом возвращает специальный массив из двух элементов. В первом элементе массива будет хранится текущее значение стейта, а во втором - функция для изменения стейта.

Для корректной работы стейтов их нельзя менять напрямую, а следует пользоваться функцией для их изменения - только тогда будет работать реактивность.

Пример
Воспользуемся функцией useState для создания стейта, содержащего название продукта:

const state = useState('prod');
В результате константа state будет представлять собой массив, в первом элементе которого будет хранится название продукта, а во втором - функция для изменения названия:

const state = useState('prod');
const name = state[0];
const setName = state[1];
Для краткости обычно не используют промежуточную константу для массива, а используют деструктуризацию:

const [name, setName] = useState('prod');
Давайте теперь выведем наш стейт с именем продукта в какой-нибудь верстке:

return <p>
<span>{name}</span>
</p>;
Соберем все вместе и получим следующий код:

import React, { useState } from 'react';

function App() {
const [name, setName] = useState('prod');

return <div>
<span>{name}</span>
</div>;
}

export default App;
Если запустить этот код, то изначально в диве выведется начальное значение стейта, в нашем случае 'prod'. При изменение стейта через функцию setName и верстке автоматически появится новое значение стейта.

Попробуем реактивность
Сделаем кнопку, по нажатию на которую будет изменяться наш стейт:

<div>
<span>{name}</span>
<button onClick={clickHandler}>btn</button>
</div>;
В обработчике клика используем функцию setName, чтобы установить продукту новое название:

function clickHandler() {
setName('xxxx');
}
Соберем весь наш код вместе. Получится, что после нажатия на кнопку текст в спене мгновенно поменяется на новое значение:

function App() {
let [name, setName] = useState('prod');

function clickHandler() {
setName('xxxx');
}

return <div>
<span>{name}</span>
<button onClick={clickHandler}>btn</button>
</div>;
}
Использование отдельных функций-обработчиков не обязательно. Можно использовать анонимную стрелочную функцию:

function App() {
let [name, setName] = useState('prod');

return <div>
<span>{name}</span>
<button onClick={() => setName('xxxx')}>btn</button>
</div>;
}
Сделаем два стейта
Давайте сделаем один стейт для названия продукта, а второй - для цены:

function App() {
let [name, setName] = useState('prod');
let [cost, setCost] = useState('1000');

return <div>
<span>{name}</span>
<span>{cost}</span>
</div>;
}
Сделаем кнопки для изменения наших стейтов:

function App() {
let [name, setName] = useState('prod');
let [cost, setCost] = useState('1000');

function clickHandler1() {
setName('xxxx');
}
function clickHandler2() {
setCost('2000');
}

return <div>
<span>{name}</span>
<span>{cost}</span>

<button onClick={clickHandler1}>btn1</button>
<button onClick={clickHandler2}>btn2</button>
</div>;
}
Перепишем на сокращенный вариант:

function App() {
let [name, setName] = useState('prod');
let [cost, setCost] = useState('1000');

return <div>
<span>{name}</span>
<span>{cost}</span>

<button onClick={() => setName('xxxx')}>btn1</button>
<button onClick={() => setCost('2000')}>btn2</button>
</div>;
}
Логическое значение в стейте
Давайте сделаем стейт inCart, который будет показывать, в корзине продукт или нет:

function App() {
let [inCart, setInCart] = useState(false);

return <div>

</div>;
}
Пусть значение false значит, что продукт не в корзине, а значение true - что в корзине. Выведем информацию об этом с помощью тернарного оператора:

function App() {
let [inCart, setInCart] = useState(false);

return <div>
<span>{inCart ? 'в корзине' : 'не в корзине'}</span>
</div>;
}
А теперь сделаем кнопку, по нажатию на которую продукт добавится в корзину:

function App() {
let [inCart, setInCart] = useState(false);

return <div>
<span>{inCart ? 'в корзине' : 'не в корзине'}</span>
<button onClick={() => setInCart(true)}>btn</button>
</div>;
}
Модифицируем наш код так, чтобы первое нажатие на кнопку добавляло в корзину, а второе - удаляло из нее:

function App() {
let [inCart, setInCart] = useState(false);

return <div>
<span>{inCart ? 'в корзине' : 'не в корзине'}</span>
<button onClick={() => setInCart(!inCart)}>btn</button>
</div>;
}
Счетчик
Давайте сделаем счетчик кликов по кнопке:

function App() {
let [count, setCount] = useState(0);

function clickHandler() {
setCount(count + 1);
}

return <div>
<span>{count}</span>
<button onClick={clickHandler}>+</button>
</div>;
}
Можно избавится от функции-обработчика, заменив ее анонимной стрелочной функцией:

function App() {
let [count, setCount] = useState(0);

return <div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>+</button>
</div>;
}