React Hooks са функции, които ви позволяват да използвате методи за състояние и жизнен цикъл на React с функционални компоненти. Беше представен през 2018 г. с React 16.8.0. Преди това единственият начин за използване на състоянието беше чрез компоненти на клас. Сега няма нужда да преобразувате функционален компонент в класов компонент, за да манипулирате състоянието.

Куките се превърнаха в популярен начин за кодиране и има защо. Те намаляват броя на концепциите, с които трябва да жонглирате, когато пишете React приложения. Край на превключването напред-назад между функции, класове, компоненти от по-висок порядък и подпори за изобразяване. Те са идеални, ако сте написали функционален компонент и осъзнавате, че трябва да добавите състояние към него.

Куките също ви позволяват да разделите компонент на по-малки функции въз основа на свързаните с него части. При извличане на данни кодът може да се съдържа в една функция, за разлика от дублирането му вътре в методите на жизнения цикъл като componentDidMount и componentDidUpdate. Локалното състояние също може да се управлява с редуктор, за да стане по-предвидимо.

Когато създавам компонент на React, започвам със създаване на клас като начин да покрия моите бази. С клас, който знам, мога да декларирам състояние и да използвам методи на жизнения цикъл, без да се налага да се притеснявам за превключване от функция към клас в средата на моя проект. Но при скорошна компилация се натъкнах на проблеми. Когато изучавате нова технология, голяма част от времето ви прекарвате в търсене на начини за прилагане на технологията в гугъл. Когато проучвах ресурси, за да създам приложението си, открих, че повечето от решенията на React, на които попаднах, използват функционални компоненти. На пръв поглед това не беше причина за безпокойство, но нещата излязоха извън контрол, когато разбрах, че функционалните компоненти манипулират състоянието и използват методи като useState! Как беше възможно това? Какво беше useState? Така се запознах с куките.

По-долу ще преобразуваме функционален компонент в компонент на клас

За скорошен групов проект създадох приложение React Native, което генерира списък с любими потребители, когато потребител е влязъл. Това е кодът за UserFavesScreen:

Когато приключим с преработването на този код, приложението ще има същата функционалност, но с по-кратък и по-организиран код.

Това е компонент на клас, който приема props като аргумент, декларира състояние, има няколко метода, които трябва да бъдат обвързани в конструктора и използва жизнения цикъл на componentDidMount за създаване на ефекти. Тъй като трябва да използваме състояние и метод на жизнения цикъл, ще импортираме useState и useEffect от react.

import React, { useState, useEffect } from "react"

След това рефакторирам кода си във функция — конвертирам всички методи, премахвам изобразяването и коментирам кода, който трябва да се върна и рефакторирам. Тъй като конструкторът има подпори, предадени към него, когато рефакторирам класа си, трябва да добавя подпорки като параметър на функцията. (Забележка: обичам да преработвам по-трудния код в края.)

След това ще декларирам състоянието си с куката useState.

useState връща двойка — текущата стойност на състоянието и функция, която ви позволява да я актуализирате. Може да се извика от манипулатор на събития или някъде другаде. Той не обединява старо и ново състояние заедно и може да бъде извикан повече от веднъж в един компонент. Той използва синтаксис за деструктуриране на масив, който дава различни имена на променливите на състоянието, които декларираме, като извикаме useState. Тези имена не са част от useState API.

useState приема първоначалната стойност и винаги, когато трябва да промените състоянието, просто извиквате деструктуриране на втория елемент в масива.

const [refreshing, setRefreshing] = useState(false);
const [favorites, setFavorites] = useState([]);

Сега, след като се погрижихме за нашето състояние, можем да продължим и да премахнем всички препратки към this в компонента. Тъй като нашият компонент вече не е клас, няма нужда от this.state или this.props. Вместо това използваме името на променливата на състоянието и вместо да използваме this.setState, ще извикаме втората променлива за задаване на състояние.

Тъй като методите на жизнения цикъл не могат да се използват вътре във функционални компоненти, ние използваме useEffect вместо това за извършване на странични ефекти във функционални компоненти. Страничните ефекти са операции, при които извличате данни или ръчно променяте DOM - операции, които могат да повлияят на други компоненти и не могат да бъдат извършени по време на рендиране. Куки като useEffect, useContext ви позволяват да организирате ефекти в компонент чрез части, които са свързани помежду си, вместо да е необходимо да ги разделяте въз основа на методи на жизнения цикъл.

useEffect приема обратно извикване като параметър. React изпълнява ефектите след всяко изобразяване на компонента, включително първото изобразяване!

Но нещата стават трудни, когато трябва да използваме async/await. Оригиналният код, който се опитваме да преработим, изпълнява асинхронна функция в компонента DidMount. Изглежда лесно, защо не можем просто да го пренапишем така:

Горният код връща грешката: „Една функция за ефект не трябва да връща нищо освен функция, която се използва за почистване.“ useEffect не може да върне обещание, вместо това ще напишем функция вътре в тази кука и ще извикаме тази функция вътре в нея.

Това е по-добре, но какво се случва, когато потребителят излезе или когато влезе нов потребител? Екранът с предпочитани потребители все още е попълнен с данните на предишния потребител. Когато стартирате приложението за първи път, това означава, че екранът винаги ще бъде празен!

Тук идват зависимостите.

Вторият незадължителен параметър на useEffect е масив от зависимости. Когато държим масива празен, ние молим useEffect да се стартира веднъж, когато компонентът се зареди и да не се изпълнява отново. Когато променлива е поставена вътре в тази кука, useEffect ще се изпълнява всеки път, когато тази променлива се промени. В нашия случай този компонент зависи от потребителя, по-специално от props.user.uid. С поставянето на props.user.uid ние молим useEffect да се изпълнява всеки път, когато потребителят се промени.

Това лесно може да стане объркващо. Добро общо правило, което трябва да следвате, е, че всеки път, когато променлива бъде изтеглена в useEffect извън нейния обхват, трябва да я включите в масива от зависимости. Това ще заяви, че useEffect зависи от тази променлива и всеки път, когато стойността на тази променлива се промени, useEffect ще се актуализира и стартира отново.

Ето крайния код:

И ето го – конвертиране на компонент от клас, който управлява състоянието и използва асинхронен компонентDidMount, във функционален компонент.

Пълният код може да бъде намерен тук.