Статья поможет новичкам понять как работать с хуками, а также будет полезна и опытным разработчикам. Этой статьей открываю серию статей про хуки. Использование хуков с одной стороны позволяет использовать методы жизненного цикла в функциональных компонентах и призваны улучшать производительность, что делает функциональные компоненты полноценным конкурентом классовых компонентов. С другой стороны, неправильное использование хуков приводит к лишним операциям и может свести на нет все преимущества функциональных компонентов.  В этой серии статей разберем основные хуки реакта и как их правильно использовать.  В серии статей поговорим про: useState, как работать с состоянием компонента, что такое "бетчинг" (batching) и для чего в качестве аргумента можно передать функцию; useEffect и как использовать cleanup и useLayoutEffect; useMemo, useCallback и почему они напрямую касаются hoc memo. Разберем ситуации когда их нужно и не нужно использовать; Context, useContext, когда использовать и улучшать производительность; useRef, использование в качестве ссылки и безопасной переменной, forwardRef и useImperativeHandle; useReducer как альтернатива useState и признаки, что пора его использовать. Также разберем нестандартных 2 случая использования useReducer; В этой статье поговорим про: Что из себя представляют хуки. Базовое использование useState, Асинхронность функции setState, Что происходит когда новое состояние равно предыдущему, В качестве начального состояния используем функцию, Хук или по-русски крючок - это функция, которая вызывается в теле функционального компонента. Как и любая функция, хук может принимать аргументы и возвращать значение. Пример хука, который возвращает значение: Пример хука, который принимает аргументы: Пример хука, который, и принимает аргументы, и возвращает значение: Реакт под капотом регистрирует все хуки, потому они должны находиться строго до любых условий (if, switch), и все хуки должны начинаться с префикса use: useState, useEffect, useMyCustomHook. Для работы с состоянием компонента используется useState. Этот хук возвращает массив из двух элементов: состояния и функции изменения состояния , принимает начальное состояние: useState(initialValue). Внутри компонента используется так: state - это обычная переменная, с ней можно работать, как и с любой другой переменной: state.toString()Может быть любым типом данных. Не пытайтесь изменять состояние вручную state = newState, это нарушит работу вашего приложения. Единственный правильный путь - использовать функцию setState. setState - это функция, которая принимает новое состояние (метка 1 и 2), либо функцию, которая принимает предыдущее состояние и возвращает новое состояние (метка 3). Главное - вызов функции запускает весь код функционального компонента повторно. В примере выше весь код, начиная с 3 по 16 строку будет вызван еще раз. Кстати, как считаете можно ли использовать другие названия переменных, кроме state и setState? Например value и setValue?  Даю 3 секунды подумать. 3 2 1 Названия может быть любыми, более того часто в одном компоненте используется несколько useState и потому необходимо давать им разные названия. setState - асинхронная функция. Под капотом реакт объединяет все мутации состояний, благодаря чему код функционального компонента будет вызван 1 раз, это называется "batching". Есть хорошая статья на эту тему. Консоль на строке 12 будет вызвана при монтировании компонента 1 раз и только 1 раз после нажатия на кнопку.  У этой особенности есть следствие. Как считаете, на сколько изменится счетчик при нажатии на кнопку test 0 и test 1? При нажатии на test 1 счетчик увеличится на 3, а при нажатии на test 0 только на 1. Почему это происходит: Поэтому если новое состояние опирается на предыдущее состояние, используйте функцию: Взгляните еще раз на этот код: Нажмем кнопку установить false 3 раза, как думаете сколько раз обновится компонент? Даю 3 секунды подумать: 3 2 1 Компонент обновится только 1 раз. Под капотом происходит сравнение предыдущего состояние и нового prevState === newState, если результатом будет true, компонент не будет обновляться. Теперь вопрос, если новое состояние объект, будет обновляться компонент или нет? setState({}); 3 2 1 Замените prevState === newState на {} === {} и станет очевидно, что обновление будет происходить, потому что объекты, массивы и функции - это ссылочные типы данных: даже когда они выглядят одинаково, они ссылаются на разные ячейки памяти, поэтому они не равны. Если мы не хотим обновлять состояние когда объекты равны по содержимому, можно сравнить предыдущее состояние с текущим и если они равны, использовать предыдущее. Обратите внимание, если хотим чтобы не произошло обновления компонента, нужно вернуть предыдущее состояние return prevState, а не его копию return { ...prevState }. Сравнивать по отдельности каждое свойство объектов неудобно, для этих целей рекомендую библиотеку fast-deep-equal. В качестве начального значения useState может принимать не только само значение, но и функцию, которая вернет начальное значение. Типы useState: Один из вариантов, когда хотим переиспользовать некоторую функцию, которая возвращает состояние. Например, когда берем начальное состояние из локального хранилища браузера: Обратите внимание, необязательно в useState передавать результат вызова функции, достаточно передать функции и он сам ее вызовет: useState(getStoredState()) → useState(getStoredState). Второй вариант - когда обрабатываем какие-то данные перед тем, как записать их в качестве начального состояния. Например, компонент принимает в качестве пропсов какие-то данные, но внутри себя, преобразовывает их в другую структуру. Важно понимать, что при любом обновлении компонента, выполняется весь код этого компонента, в том числе и prepareData. Однако начальное значение состояние принимается только при монтировании компонента, при других обновлениях компонента - игнорируется. Потому функция prepareData будет выполняться всегда, хотя ее результат будет использован только 1 раз. Это ухудшает производительность. Что нужно сделать? Достаточно в качестве initialState передавать не prepareData(data), а функцию, которая вернет prepareData(data). Хук (по-русски крючок) - это функция. Как и любая функция, он может принимать аргументы и возвращать значение. Реакт регистрирует все хуки компонента, потому они должны быть до любых условий (if, switch) и начинаться с префикса use. Хук useState возвращает массив из двух элементов (состояние, функция изменения состояние), а принимает начальное состояние. Состояние - может быть чем угодно: строкой, числом, массивом и т.д. и с ним можно работать как с любой другой переменной, но изменять только с помощью функции изменения состояния. Функция изменения состояния, принимает как новое значение setState(newState), так и функцию, которая получает предыдущее состояние, а результат ее вызова будет новым состоянием: setState(previosState => newState). Функция изменения состояния - асинхронна, реакт объединяет несколько изменений состояния в один цикл обновления компонента. Потому любые счетчики setState(prevCount => prevCount + 1), переключатели setState(prevValue => !prevValue) должны опираться на предыдущее состояние, иначе это может привести к непредсказуемым ошибкам.  Хук useState обновляет компонент только если новое состояние не равно предыдущему. Проверка осуществляется по строгому равенству prevState === newState. В качестве начального состояния можно передавать функцию, которая вернет начальное состояние useState(getStoredState). Это удобно, когда нам нужно переиспользовать эту функцию. Если статья показалась полезной и интересной, ставьте палец вверх. Если есть вопросы - пишите в комментариях. Также хочу пригласить всех желающих на бесплатный урок, который проведет мой коллега на платформе OTUS. В рамках урока вы узнаете для чего разработчику на React.js умение писать тесты и как применять React Testing Library в процессе разработки.   Зарегистрироваться на урок.