Функциональные компоненты не всегда были предпочтительным методом объявления компонентов в React.
До появления версии 16.8 React к функциональным компонентам относились во многом как к второсортным. Они не могли обрабатывать состояние, логику и множество других функций реагирования, и мы использовали их только для визуализации очень простых компонентов пользовательского интерфейса.
React версии 16.8 решил эти проблемы, представив React Hooks, которые позволили разработчикам использовать эти функции реагирования в функциональных компонентах.
Что такое React-хуки?
Хуки — это встроенные функции React, представленные в React версии 16.8. Они позволяют вам использовать функции библиотеки React, такие как методы жизненного цикла, состояние и контекст в функциональных компонентах, не беспокоясь о переписывании их в класс.
Каждое имя React Hook начинается со слова "use". Например, useStateили useEffect. Этот формат был выбран потому, что хуки позволяют разработчикам использовать специальные возможности библиотеки React. Итак, вы используете эту особенность use библиотеки React.
Зачем использовать React Hooks?
Многие разработчики скептически относятся к изучению React Hooks. Но вы не должны быть. Вот несколько причин, по которым вы должны начать использовать React Hooks:
- Классы в React могут быть довольно запутанными
Классы мешают правильному изучению React. Чтобы их использовать, нужно понимать, как работает ключевое слово this. Также нужно постоянно помнить о привязке обработчиков событий, а также других избыточных методов, встречающихся при работе с классами в React.
- Компоненты классов сложны и могут быть трудны для понимания
Компоненты класса обычно большие и пытаются выполнять множество операций. В долгосрочной перспективе их становится трудно понять.
Хуки решают эту проблему, позволяя вам разделять большие компоненты на различные более мелкие функции, вместо того, чтобы заставлять всю логику помещаться в один компонент.
- Хуки имеют более короткие компоненты и лучшую читабельность
Компоненты класса поставляются с большим количеством шаблонного кода. Рассмотрим компонент счетчика ниже:
class Counter extends Component {
constructor(props) {
super(props)
this.state = {
count: 1,
}
}
render() {
return (
<div>
The Current Count: {this.state.count}
<div>
<button onClick={this.setState({ count: this.state.count - 1 })}>
add
</button>
<button onClick={this.setState({ count: this.state.count + 1 })}>
subtract
</button>
</div>
</div>
);
}
}
Вот эквивалентный код с использованием функционального компонента и React Hooks:
function Counter () {
const [count, setCount] = useState(1);
return (
<div>
The Current Count: {this.state.count}
<div>
<button onClick={() => setCount(count + 1)}>add</button>
<button onClick={() => setCount(count - 1)}>subtract</button>
</div>
</div>
);
};
Обратите внимание, что компонент класса намного сложнее. Вам нужен класс для расширения React, конструктор для инициализации состояния, и вам нужно везде ссылаться на ключевое слово this.
Использование функциональных компонентов устраняет многое из этого, поэтому наш код становится короче, его легче читать и поддерживать.
Правила использования React Hooks
При использовании React Hooks необходимо придерживаться нескольких правил:
- Вызывайте хуки только на верхнем уровне компонента: вы не должны использовать хуки внутри циклов, условий или вложенных функций. Вместо этого всегда используйте хуки на верхнем уровне вашей функции React перед любым ключевым словом return.
- Вызывайте хуки только из функций React. Никогда не вызывайте хуки из обычных функций JavaScript. Вы можете:
- Вызывать хуки из функциональных компонентов React.
- Выполнить вызов хуков из пользовательских хуков.
Самые распространенные хуки React
На сегодняшний день React имеет 10 встроенных хуков. Рассмотрим четыре самых распространенных:
- useState
- useEffect
- useContext
- useReducer
Хук useState позволяет создавать, обновлять и управлять состоянием внутри функциональных компонентов.
В React есть концепция состояния, которая представляет собой переменные, содержащие данные, от которых зависят наши компоненты и которые могут меняться со временем. Всякий раз, когда эти переменные изменяются, React обновляет пользовательский интерфейс, повторно отображая компонент в DOM с текущими значениями переменных состояния.
Хук принимает единственный необязательный аргумент: начальное значение для состояния. Затем он возвращает массив из двух значений:
- Переменная состояния
- Функция для обновления состояния
В качестве примера рассмотрим компонент счетчика:
Чтобы использовать хук, первым делом нужно импортировать хук вверху файла:
import { useState } from "react";
Затем инициализируйте хук со значением. Поскольку он возвращает массив, вы можете использовать деструктуризацию массива для доступа к отдельным элементам в массиве, например:
const [count, setCount] = useState(0);
При этом код компонента будет таким:
import { useState } from "react";
function Counter() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
Current Cart Count: {count}
<div>
<button onClick={() => setCount(count - 1)}>Add to cart</button>
<button onClick={() => setCount(count + 1)}>Remove from cart</button>
</div>
</div>
);
}
При нажатии кнопки « Добавить в корзину » или « Удалить из корзины » значение счетчика переменной состояния изменится, и компонент будет повторно визуализирован с обновленным значением состояния.
Если вы знакомы с методами жизненного цикла класса React, вы можете думать о useEffectHook как о методах componentDidMount, componentDidUpdateи componentWillUnmountжизненного цикла, объединенных в одной функции. Он позволяет воспроизводить методы жизненного цикла React в функциональных компонентах.
Хук useEffectпозволяет выполнять побочные эффекты в функциональных компонентах. Побочные эффекты — это действия, которые могут выполняться параллельно с основными операциями компонента, такими как взаимодействие с внешним API, изменение переменных состояния и выборка данных.
Хук useEffect, принимает 2 аргумента:
- Функция с кодом для запуска
- Массив, содержащий список значений из области действия компонента (реквизиты, контекст и переменные состояния), известный как массив зависимостей, который указывает хуку запускаться каждый раз, когда его значение обновляется. Если не указан, хук будет запускаться после каждого рендера.
Вот пример использования хука:
import { useState, useEffect } from "react";
function Counter() {
// Declare state variables
const [count, setCount] = useState(0);
const [product, setProduct] = useState("Eggs");
useEffect(() => {
console.log(`${product} will rule the world!`);
});
return (
<div>
Current {product}'s count: {count}
<div>
<button onClick={() => setCount(count + 1)}>Add to cart</button>
<button onClick={() => setCount(count - 1)}>Remove from cart</button>
Change Product:{" "}
<input type="text" onChange={(e) => setProduct(e.target.value)} />
</div>
</div>
);
}
Как условно активировать эффект?
Чтобы запускать хук только при изменении определенных значений, передайте переменные как зависимость в массив:
useEffect(() => {
console.log(`${product} will rule the world!`);
}, [product]); // Only re-run the effect if the value of product changes
С этим изменением хук будет работать только при первом рендеринге и при изменении значения product.
Как запустить один раз при первом рендеринге?
Если вы хотите, чтобы эффект выполнялся только один раз при первом рендеринге, например, вызовы API при первом рендеринге компонента, вы можете передать пустой массив в качестве его зависимости следующим образом:
useEffect(() => {
console.log("This runs once on first render");
}, []);
Предоставляя пустой массив, он указывает хуку прослушивать нулевое изменение состояния, поэтому он будет запущен только один раз.
Хук useContext, работает с React Context API . Он позволяет сделать определенные данные доступными для всех компонентов приложения, независимо от того, насколько глубоко они вложены друг в друга.
React имеет однонаправленный поток данных, в котором данные могут передаваться только от родителя к дочернему элементу. Чтобы передать данные (например, состояние) от родителя к дочернему компоненту, вам нужно вручную передать их в качестве реквизита через различные уровни в зависимости от того, насколько глубоко вложен дочерний компонент.
Для таких данных, как предпочитаемый пользователем язык, тема или свойства аутентифицированного пользователя, утомительно передавать их вручную вниз по дереву компонентов.
Контекстный API React и хук useContext, упрощают передачу данных между всеми компонентами приложения.
Он принимает объект контекста, созданный с помощью React.createContext, и возвращает текущий контекст следующим образом:
const value = useContext(SomeContext);
Давайте рассмотрим пример того, как работает хук:
Во-первых, создайте контекст для использования хука. Например, вот UserContext для получения значения текущих пользователей:
import React from "react";
// some mock context values
const users = [
{
name: "Harry Potter",
occupation: "Wizard",
},
{
name: "Kent Clark",
occupation: "Super hero",
},
];
export const UserContext = React.createContext(users);
У каждого контекста есть оболочка Provider, которая позволяет его дочерним компонентам подписываться на изменения в контексте и передавать значение контекста через свойство value.
Если свойство value поставщика обновлено, его потребляющие дочерние компоненты будут повторно отображаться с новым значением контекста.
function Users() {
return (
<UserContext.Provider value={users}>
<UserProfile />
</UserContext.Provider>
);
}
В примере UserProfileсделан потребляющий компонент контекста.
import React, { useContext } from "react";
import { UserContext } from "./App";
export function UserProfile() {
const users = useContext(UserContext);
return (
<div>
<li>
I am {
user.name} and I am a {user.occupation}!
</li>
))}
</div>
);
}
Хук useReducer, является альтернативой useState. Разница в том, что он допускает более сложную логику и обновления состояния, которые включают несколько подзначений.
Подобно useState, useReducerпозволяет создавать переменные, подобные состоянию, которые вызывают обновление пользовательского интерфейса при каждом их изменении.
Этот хук принимает 2 аргумента: функцию-редуктор и начальное состояние.
useReducer(reducer, initialState);
Он возвращает массив из двух значений, которые можно деструктурировать до текущего значения состояния и функции отправки.
const [state, dispatch] = useReducer(reducer, initialState);
Давайте узнаем о его аргументах и возвращаемых значениях:
- state: это текущее значение initialState, переданное в хук.
- reducer: Редуктор — это функция, которая принимает состояние и действие. На основе этих аргументов он определяет, как изменится значение состояния.
- dispatch: функция диспетчеризации — это то, как мы передаем действие функции редуктора. Он отправляет действие, которое будет использоваться для обновления состояния.
Как правило, мы перебираем тип действий, которые мы сделали в нашем приложении, с помощью оператора switch, чтобы определить, как изменится значение состояния. Вот как хук обновляет значения своего состояния.
function reducer(state, action) {
switch (action.type) {
case "CASE_1":
return {
updatedState,
};
case "CASE_2":
return {
updatedState,
};
default:
return state;
}
}
Функция отправки обычно отправляет объект в формате:
dispatch({ type: "ACTION_TYPE", payload: optionalArguments });
Где тип — это описание действия, а полезная нагрузка — это аргументы, которые вы хотите передать редюсеру.
Как создавать собственные хуки
Пользовательский хук — это идея извлечения часто используемой логики компонентов из пользовательского интерфейса в функции JavaScript с использованием уже доступных хуков React. Это помогает предотвратить дублирование кода и позволяет многократно использовать такую логику в нескольких компонентах.
Давайте рассмотрим пример пользовательского хука, который будет возвращать ответ с любого допустимого URL-адреса API, который мы ему передаем.
import { useState, useEffect } from "react";
export function useFetch(url) {
//values
const [data, setData] = useState(null);
const [error, setError] = useState("");
useEffect(() => {
fetch(url)
.then(res => {
if (!res.ok) {
throw Error("something wrong, çould not connect to resource");
}
setData(res.json());
})
.then(() => {
setError("");
})
.catch( error => {
console.warn(`sorry an error occurred, due to ${error.message} `);
setData(null);
setError(error.message);
});
}, [url]);
return [data, error];
}
Теперь вы можете использовать эту логику в любом месте вашего приложения, просто импортировав функцию и передав путь API в качестве аргумента, вместо того, чтобы писать все с нуля.
Подведение итогов
Надеюсь, вы увидели, насколько полезны React Hooks. Они позволяют создавать эффективные компоненты на лету, не беспокоясь о проблемах, связанных с классовыми компонентами.
От возможности сосредоточиться на написании основного кода до создания собственных пользовательских хуков... React Hooks — это здорово!