Начало работы с API React Hooks
Опубликовано: 2022-03-10Когда React 16.8 был официально выпущен в начале февраля 2019 года, он поставлялся с дополнительным API, который позволяет вам использовать состояние и другие функции в React без написания класса. Этот дополнительный API называется Hooks , и они становятся популярными в экосистеме React, от проектов с открытым исходным кодом до использования в производственных приложениях.
React Hooks полностью опциональны, что означает, что переписывание существующего кода не требуется, они не содержат критических изменений и доступны для использования с выпуском React 16.8. Некоторые любопытные разработчики использовали Hooks API еще до того, как он был официально выпущен, но тогда он не был стабильным и был лишь экспериментальной функцией. Теперь он стабилен и рекомендуется для использования разработчиками React.
Примечание : мы не будем говорить о React или JavaScript в целом. Хорошее знание ReactJS и JavaScript пригодится вам при работе с этим руководством.
Что такое React-хуки?
React Hooks — это встроенные функции, которые позволяют разработчикам React использовать методы состояния и жизненного цикла внутри функциональных компонентов. Они также работают вместе с существующим кодом, поэтому их можно легко внедрить в кодовую базу. То, как хуки были представлены публике, заключалось в том, что они позволяют разработчикам использовать состояние в функциональных компонентах, но внутри хуки гораздо мощнее. Они позволяют разработчикам React пользоваться следующими преимуществами:
- Улучшено повторное использование кода;
- Улучшенная композиция кода;
- Улучшенные значения по умолчанию;
- Совместное использование невизуальной логики с использованием пользовательских хуков;
- Гибкость перемещения вверх и вниз по дереву
components
.
С React Hooks разработчики получают возможность использовать функциональные компоненты практически для всего, что им нужно, от простого рендеринга пользовательского интерфейса до обработки состояния и логики — что довольно удобно.
Мотивация, стоящая за выпуском React Hooks
Согласно официальной документации ReactJS, выпуск React Hooks вызван следующими причинами:
- Повторное использование логики с отслеживанием состояния между компонентами затруднено.
С помощью хуков вы можете повторно использовать логику между своими компонентами, не изменяя их архитектуру или структуру. - Сложные компоненты могут быть трудными для понимания.
Когда компоненты становятся больше и выполняют множество операций, в долгосрочной перспективе их становится трудно понять. Хуки решают эту проблему, позволяя вам разделить конкретный компонент на различные более мелкие функции в зависимости от того, какие части этого отдельного компонента связаны (например, настройка подписки или выборка данных), вместо того, чтобы принудительно разделять на основе методов жизненного цикла. - Классы довольно запутанные.
Классы мешают правильному изучению React; вам нужно будет понять, какthis
работает в JavaScript, что отличается от других языков. React Hooks решает эту проблему, позволяя разработчикам использовать лучшие функции React без использования классов.
Правила крючков
Есть два основных правила, которых необходимо строго придерживаться, как указано основной командой React, которые они изложили в документации предложения хуков.
- Не используйте хуки внутри циклов, условий или вложенных функций;
- Используйте хуки только внутри React Functions.
Основные хуки React
Есть 10 встроенных хуков, которые поставляются с React 16.8, но основные (обычно используемые) хуки включают в себя:
-
useState()
-
useEffect()
-
useContext()
-
useReducer()
Это 4 основных хука, которые обычно используются разработчиками React, внедрившими React Hooks в свои кодовые базы.
useState()
useState()
позволяет разработчикам React обновлять, обрабатывать и манипулировать состоянием внутри функциональных компонентов без необходимости преобразовывать его в компонент класса. Давайте используем приведенный ниже фрагмент кода — простой компонент счетчика возраста, и мы будем использовать его для объяснения возможностей и синтаксиса useState()
.
function App() { const [age, setAge] = useState(19); const handleClick = () => setAge(age + 1) return <div> I am {age} Years Old <div> <button onClick={handleClick}>Increase my age! </button> </div> </div> }
Если вы заметили, наш компонент выглядит довольно просто, лаконично, и теперь он является функциональным компонентом, а также не имеет того уровня сложности, который был бы у компонента класса.
useState()
получает начальное состояние в качестве аргумента, а затем возвращает, используя деструктурирование массива в JavaScript, две переменные в массиве могут быть названы what. Первая переменная — это фактическое состояние, а вторая переменная — это функция, предназначенная для обновления состояния путем предоставления нового состояния.
Вот как должен выглядеть наш компонент при рендеринге в нашем приложении React. Нажав на кнопку «Увеличить мой возраст», состояние возраста изменится, и компонент будет работать так же, как компонент класса с состоянием.
useEffect()
useEffect()
принимает функцию, которая будет содержать эффективный код. В функциональных компонентах такие эффекты, как мутации, подписки, таймеры, ведение журнала и другие эффекты, не могут быть размещены внутри функционального компонента, потому что это может привести к множеству несоответствий при отображении пользовательского интерфейса, а также к запутанным ошибкам.
При использовании useEffect()
переданная в него эффективная функция будет выполняться сразу после отображения рендера на экране. Эффекты в основном заглядывают в императивный способ создания пользовательских интерфейсов, который сильно отличается от функционального способа React.
По умолчанию эффекты выполняются в основном после завершения рендеринга, но вы также можете активировать их при изменении определенных значений.
useEffect()
в основном используется для побочных эффектов, которые обычно используются для взаимодействия с Browser/DOM API или внешней API-подобной выборкой данных или подписками. Кроме того, если вы уже знакомы с тем, как работают методы жизненного цикла React, вы также можете рассматривать useEffect()
как монтирование , обновление и размонтирование компонентов — все это объединено в одной функции. Это позволяет нам воспроизводить методы жизненного цикла в функциональных компонентах.
Мы будем использовать фрагменты кода ниже, чтобы объяснить самый простой способ, который мы можем использовать, используя useEffect()
.
Шаг 1: Определите состояние вашего приложения
import React, {useState} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Точно так же, как мы обсуждали в предыдущем разделе, как использовать useState()
для обработки состояния внутри функциональных компонентов, мы использовали его в нашем фрагменте кода, чтобы установить состояние для нашего приложения, которое отображает мое полное имя.
Шаг 2: вызов хука useEffect
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({FirstName: 'Shedrack', surname: 'Akintayo'}) }, [])//pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Теперь мы импортировали хук useEffect
, а также использовали useEffect()
для установки состояния нашего свойства имени и фамилии, что довольно аккуратно и лаконично.
Вы могли заметить хук useEffect
во втором аргументе, который представляет собой пустой массив; это потому, что он содержит вызов setFullName
, у которого нет списка зависимостей. Передача второго аргумента предотвратит бесконечную цепочку обновлений ( componentDidUpdate()
), а также позволит нашему useEffect()
действовать как метод жизненного цикла componentDidMount
и выполнять рендеринг один раз без повторного рендеринга при каждом изменении в дереве.
Наше приложение React теперь должно выглядеть так:
Мы также можем использовать изменение свойства title
нашего приложения внутри функции useEffect()
, вызвав функцию setTitle()
, например так:
import React, {useState, useEffect} from 'react'; function App() { //Define State const [name, setName] = useState({firstName: 'name', surname: 'surname'}); const [title, setTitle] = useState('BIO'); //Call the use effect hook useEffect(() => { setName({firstName: 'Shedrack', surname: 'Akintayo'}) setTitle({'My Full Name'}) //Set Title }, [])// pass in an empty array as a second argument return( <div> <h1>Title: {title}</h1> <h3>Name: {name.firstName}</h3> <h3>Surname: {name.surname}</h3> </div> ); }; export default App
Теперь, после повторного рендеринга нашего приложения, оно показывает новый заголовок.
useContext()
useContext()
принимает объект контекста, то есть значение, которое возвращается из React.createContext
, а затем возвращает текущее значение контекста для этого контекста.
Этот хук дает функциональным компонентам легкий доступ к контексту вашего приложения React. До того, как был введен хук useContext
, вам нужно было настроить contextType
или <Consumer>
для доступа к вашему глобальному состоянию, переданному от какого-либо провайдера в компоненте класса.
По сути, хук useContext
работает с React Context API, который позволяет глубоко обмениваться данными в вашем приложении без необходимости вручную передавать реквизиты вашего приложения на различные уровни. Теперь useContext()
делает использование Context немного проще.
Приведенные ниже фрагменты кода показывают, как работает Context API и как useContext
делает его лучше.
Обычный способ использования контекстного API
import React from "react"; import ReactDOM from "react-dom"; const NumberContext = React.createContext(); function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); } function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Давайте теперь разберем фрагмент кода и объясним каждую концепцию.
Ниже мы создаем контекст с именем NumberContext
. Он предназначен для возврата объекта с двумя значениями: { Provider, Consumer }
.
const NumberContext = React.createContext();
Затем мы используем значение Provider
, которое было возвращено из созданного нами NumberContext
, чтобы сделать конкретное значение доступным для всех дочерних элементов.
function App() { return ( <NumberContext.Provider value={45}> <div> <Display /> </div> </NumberContext.Provider> ); }
При этом мы можем использовать значение Consumer
, которое было возвращено из созданного нами NumberContext
, чтобы получить значение, которое мы сделали доступным для всех дочерних элементов. Если вы заметили, этот компонент не получил никакого реквизита.
function Display() { return ( <NumberContext.Consumer> {value => <div>The answer to the question is {value}.</div>} </NumberContext.Consumer> ); } ReactDOM.render(<App />, document.querySelector("#root"));
Обратите внимание, как мы смогли получить значение из компонента App
в компонент Display
, обернув наше содержимое в NumberContext.Consumer
и используя метод render props для извлечения значения и его рендеринга.
Все работает хорошо, и метод render props, который мы использовали, является действительно хорошим шаблоном для обработки динамических данных, но в долгосрочной перспективе он вносит некоторую ненужную вложенность и путаницу, если вы к этому не привыкли.
Использование метода useContext
Чтобы объяснить метод useContext
, мы перепишем компонент Display
, используя хук useContext.
// import useContext (or we could write React.useContext) import React, { useContext } from 'react'; // old code goes here function Display() { const value = useContext(NumberContext); return <div>The answer is {value}.</div>; }
Это все, что нам нужно сделать, чтобы отобразить нашу ценность. Довольно аккуратно, правда? Вы вызываете useContext()
и передаете объект контекста, который мы создали, и мы извлекаем из него значение.
Примечание. Не забывайте, что аргумент, передаваемый хуку useContext, должен быть самим объектом контекста, и любой компонент, вызывающий useContext, всегда будет перерисовываться при изменении значения контекста.
useReducer()
useReducer
используется для обработки сложных состояний и переходов в состоянии. Он принимает функцию reducer
, а также ввод начального состояния; затем он возвращает текущее состояние, а также функцию dispatch
в качестве вывода посредством деструктуризации массива.
Приведенный ниже код является правильным синтаксисом для использования хука useReducer
.
const [state, dispatch] = useReducer(reducer, initialArg, init);
Это своего рода альтернатива useState
; обычно предпочтительнее использовать состояние, когда у вас есть сложная логика состояния, связанная с несколькими useState
, или когда следующее состояние зависит от предыдущего.
Доступны другие хуки React
useCallback | Этот хук возвращает функцию обратного вызова, которая запоминается и изменяется только в случае изменения одной зависимости в дереве зависимостей. |
useMemo | Этот хук возвращает запомненное значение, вы можете передать функцию «создать», а также массив зависимостей. Возвращаемое им значение будет снова использовать запомненное значение только в том случае, если одна из зависимостей в дереве зависимостей изменится. |
useRef | Этот хук возвращает изменяемый объект ref, чье свойство .current инициализируется переданным аргументом ( initialValue ). Возвращенный объект будет доступен в течение всего срока службы компонента. |
useImperativeHandle | Этот хук используется для настройки значения экземпляра, которое становится доступным для родительских компонентов при использовании ссылок в React. |
useLayoutEffect | Этот хук похож на хук useEffect , однако срабатывает синхронно после всех мутаций DOM. Он также отображается так же, как componentDidUpdate и componentDidMount . |
useDebugValue | Этот хук можно использовать для отображения метки для пользовательских хуков в React Dev Tools. Это очень полезно для отладки с помощью React Dev Tools. |
Пользовательские хуки React
«Пользовательский хук» — это функция JavaScript, имена которой начинаются со слова use
и могут использоваться для вызова других хуков. Он также позволяет извлекать логику компонента в повторно используемые функции; это обычные функции JavaScript, которые могут использовать внутри себя другие хуки, а также содержат общую логику с отслеживанием состояния, которую можно использовать в нескольких компонентах.
Фрагменты кода ниже демонстрируют пример пользовательского React Hook для реализации бесконечной прокрутки (автор Paulo Levy):
import { useState } from "react"; export const useInfiniteScroll = (start = 30, pace = 10) => { const [limit, setLimit] = useState(start); window.onscroll = () => { if ( window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight ) { setLimit(limit + pace); } }; return limit; };
Этот пользовательский хук принимает два аргумента: start
и pace
. Аргумент start — это начальное количество элементов, которые должны быть отображены, а аргумент темпа — это последующее количество элементов, которые должны быть отображены. По умолчанию аргументы start
и pace
установлены на 30
и 10
соответственно, что означает, что вы действительно можете вызвать хук без каких-либо аргументов, и вместо них будут использоваться эти значения по умолчанию.
Таким образом, чтобы использовать этот хук в приложении React, мы будем использовать его с онлайн-API, который возвращает «поддельные» данные:
import React, { useState, useEffect } from "react"; import { useInfiniteScroll } from "./useInfiniteScroll"; const App = () => { let infiniteScroll = useInfiniteScroll(); const [tableContent, setTableContent] = useState([]); useEffect(() => { fetch("https://jsonplaceholder.typicode.com/todos/") .then(response => response.json()) .then(json => setTableContent(json)); }, []); return ( <div style={{ textAlign: "center" }}> <table> <thead> <tr> <th>User ID</th> <th>Title</th> </tr> </thead> <tbody> {tableContent.slice(0, infiniteScroll).map(content => { return ( <tr key={content.id}> <td style={{ paddingTop: "10px" }}>{content.userId}</td> <td style={{ paddingTop: "10px" }}>{content.title}</td> </tr> ); })} </tbody> </table> </div> ); }; export default App;
Приведенный выше код отобразит список поддельных данных ( userID
и title
), которые используют хук бесконечной прокрутки для отображения начального количества данных на экране.
Заключение
Надеюсь, вам понравилось работать с этим уроком. Вы всегда можете прочитать больше о React Hooks из приведенных ниже ссылок.
Если у вас есть какие-либо вопросы, вы можете оставить их в разделе комментариев, и я буду рад ответить на каждый из них!
Вспомогательный репозиторий для этой статьи доступен на Github.
Ресурсы и дополнительная литература
- «Справочник по API хуков», React.js Docs
- «Что такое React Hooks?», Робин Вирух.
- «Как работает хук
useContext
», Дэйв Седдиа. - «React Hooks: как использовать
useEffect()
», Хоссейн Ахмади, Medium - «Написание собственных пользовательских хуков React», Аюш Джайсвал, Medium
- «Простые для понимания рецепты хуков React», Гейб Рэгланд, useHooks()