Полное руководство по React Hooks

Полное руководство по React Hooks

4 января 2024 г.

React Hooks открывает новый мир в экосистеме React. Однако это может сбить с толку новичков, впервые работающих с React.

React Hooks

Поэтому в этом уроке я помогу вам понять, что такое React Hooks, какие виды хуков доступны в React, а также когда и как их использовать. Кроме того, вы узнаете, как создавать собственные перехватчики в React.

Что такое React Hooks?

Проще говоря, React Hooks — это базовые функции JavaScript, которые управляют различными задачами в React. Да, они не так сложны, как вы думаете.

Так почему же люди так воодушевлены ими? Давайте выясним.

В традиционной разработке React управление состоянием компонентов и обработка побочных эффектов в основном выполнялись внутри компонентов классов. (Не беспокойтесь о терминах «состояние» и «побочный эффект»; мы узнаем о них позже в этом посте). Однако этот подход имел некоторые ограничения и затруднял чтение и поддержку кода.

В качестве примера просто посмотрите на приведенный ниже код и убедитесь, насколько утомительно было управлять состоянием до введения перехватчиков. Нам пришлось управлять состояниями, привязывая их к компоненту с помощью ключевого слова JavaScript.

React-Hooks

И наконец, в выпуске React v16.8 были введены перехваты для решения этих проблем и предоставлены более элегантные решения для функциональных компонентов.

Знакомство с useState и хуком useEffect

В ReactJS есть такие хуки, как useState, useEffect, useReducer, useMemo, useRef и многие другие. В этом посте мы попытаемся понять два наиболее часто используемых хука: useState и useEffect.

<блок-цитата>

Интересный факт: Знаете ли вы, что можно создавать свои собственные хуки React? Что ж, вскоре я также проведу вас через создание собственных хуков.

Понимание использования хука useState

Хук useState в React позволяет функциональному компоненту управлять данными состояния и манипулировать ими. Итак, вам может быть интересно, что это за «данные о состоянии».

Хорошо, позвольте мне сказать вам, что данные о состоянии здесь относятся к данным внутри компонента, которые изменяются со временем в результате событий или внешних эффектов, таких как вызовы HTTP.

Например, пример: нажатие на паузу в видеопроигрывателе должно приостановить текущее воспроизводимое видео, а выбор цвета в палитре цветов должен выбрать текущий цвет.

React-Hooks-for-Beginners

Для этого компонентам необходимо запомнить вот такие вещи: текущее состояние видео и текущее значение цвета.

Теперь давайте воспользуемся хуком useState на примере палитры цветов выше и шаг за шагом создадим небольшое приложение.

* Для начала создайте компонент ColorPicker в своем приложении React. А пока давайте упростим — добавьте

с текстом «React Color Picker» (не стесняйтесь использовать любой текст по вашему усмотрению).

export default function ColorPicker() {
  return (
    <main>
      <h1>React Color Picker</h1>
    </main>
  )
}

* Теперь импортируйте хук useState из React. Не забудьте вызвать хук useState внутри компонента ColorPicker. Его нельзя вызвать вне компонента ColorPicker. Здесь мы назначили перехват useState переменной colorState и записали в консоль переменную colorState.

import { useState } from "react";
// const state = useState();  ❎ Cannot be use outside ColorPicker
export default function ColorPicker() {
  const colorState = useState() //✅ Correct way of calling useState hook
  console.log('Color State : ', colorState)     
  return (
    <main>
      <h1>React Color Picker</h1>
    </main>
  )
}
  • Затем откройте консоль браузера и вы увидите что-то вроде «Состояние цвета: ▶ Массив(2)». Если вы расширите этот массив, вы увидите, что в индексе 0 находится «неопределенное», а в индексе 1 — функция.
Color State :  Array(2)
                0 : undefined
                1 : ƒ ()

* Теперь давайте деструктурируем массив, полученный с помощью перехватчика useState. Мы назначим переменные color и setColor элементам с индексами 0 и 1 соответственно, возвращаемым ловушкой useState.

import { useState } from "react";
export default function ColorPicker() {
  // const colorState = useState()
  const [color, setColor] = useState()
  return (
    <main>
      <h1>React Color Picker</h1>
    </main>
  )
}
  • Когда мы вызываем перехватчик useState, он принимает один аргумент, известный как initialState. Это начальное состояние может быть разных типов: массив, объект, строка, логическое значение, число или что угодно. В нашем примере мы устанавливаем для initialState значение «#8C00FF» и присваиваем его переменной color. Кроме того, setColor — это функция, которую мы используем для изменения значения InitialState при каждом взаимодействии с ним.
import { useState } from "react";
export default function ColorPicker() {
  // const colorState = useState()
  const [color, setColor] = useState('#8C00FF')
  return (
    <main>
      <h1>React Color Picker</h1>
    </main>
  )
}
  • Давайте сделаем следующий шаг. Добавьте
    и настройте его для отображения выбранного цвета, а также включите тег типа «color» для выбора цвета из входных данных. Установите для свойства ввода значение цвета (initialState) и прикрепите функцию onChange, называемую onColorChange(), для обновления исходного состояния.

При изменении входного значения срабатывает функция onColorChange(). Внутри этой функции мы вызываем функцию setColor и передаем ей e.target.value в качестве аргумента.

Это приведет к тому, что функция setColor обновит значение цвета, которое, в свою очередь, обновит и отразит изменение цвета везде, где оно используется.

import { useState } from "react";
export default function ColorPicker() {
  const [color, setColor] = useState('#8C00FF')
 
  function onColorChange(e){
    setColor(e.target.value)
  }
  const divStyle = {
    width : '200px',
    height : '200px',
    backgroundColor : color // color variable (#8c00ff)
  }
  return (
    <main>
      <h1>React Color Picker</h1>
        <div style={divStyle}>
          <h2>Color : {color}</h2>
        </div>
        <input type='color' value={color} onChange={onColorChange} />
    </main>
  )
}
  • Вот важный момент, который следует помнить: когда состояние изменяется (в данном случае состояние цвета), компонент перерисовывается, и в результате пользовательский интерфейс (UI) обновляется, чтобы отразить новое состояние. Такое динамическое поведение является одной из ключевых сильных сторон React и его перехватчика useState, позволяющего оперативно обновлять наши веб-приложения в режиме реального времени.

React-Hooks-for-Beginners

Понимание использования хука useEffect

useEffect Хук позволяет нам выполнять код после визуализации компонента. Это полезно для таких задач, как получение данных, если вы не используете какую-либо библиотеку управления серверными данными, такую ​​​​как React Query и т. д. Вы также можете использовать его для настройки подписок или обработки побочных эффектов, таких как использование любых внешних библиотек, например, видео-плагина.

Синтаксис перехватчика useEffect очень прост. Он принимает функцию обратного вызова и массив зависимостей, а массив зависимостей определяет, когда должен выполняться эффект.

useEffect(callbackFunction, dependencyArray);

Теперь массив зависимостей может быть либо пустым, либо мы можем передавать такие значения, как реквизиты, состояние и контексты, из области действия компонента. Перехват useEffect с пустым массивом зависимостей будет выполняться только в первый раз при отрисовке компонента, а если перехватчик useEffect имеет массив зависимостей, он будет запускаться каждый раз, когда изменяется значение в массиве зависимостей.

Hooks-in-React

<блок-цитата>

Примечание. Мы также можем пренебречь массивом зависимостей, однако это может вызвать серьезные проблемы с производительностью, поскольку эффект будет выполняться после каждого рендеринга компонента, включая начальный, без зависимости от каких-либо конкретных значений. >

Теперь давайте посмотрим, как мы можем использовать этот хук в нашем приложении.

* Создайте компонент, скажем, Todo, и импортируйте useEffect из React. Чтобы использовать useEffect, вызовите его внутри компонента Todo.

import { useEffect } from "react";
export default function Todo() {
  useEffect(() => {}, []);
//useEffect(callBackFunction, [dependenciesArray]);
  return (
    <div>
    <h1>Todo App</h1>
    </div>
  )
}
  • Давайте добавим функцию обратного вызова, которая получает некоторые данные из внешнего API.
import { useEffect } from "react";
export default function Todo() {
 
  //callBackFunction to fetch todods
  function fetchTodos(){
    fetch('https://jsonplaceholder.typicode.com/todos')
    .then(response => response.json())
    .then(json => console.log(json))
  }
  //useEffect(callBackFunction, [dependenciesArray]);
  useEffect(fetchTodos, []); 
  return (
    <div>
    <h1>Todo App</h1>
    </div>
  )
}

.

  • Давайте выведем эти данные на экран. Для этого нам нужен хук useState для хранения данных. Изначально это пустой массив, который обновляется после успешного выполнения эффекта.
import { useEffect } from "react";
export default function Todo() {
  const [todos, setTodos] = useState([])
  //callBackFunction to fetch todods
  function fetchTodos(){
    fetch('https://jsonplaceholder.typicode.com/todos')
    .then(response => response.json())
    .then(json => setTodos(json))
  }
  //useEffect(callBackFunction, [dependenciesArray]);
  useEffect(fetchTodos, []); 
  return (
    <div>
    <h1>Todo App</h1>
    </div>
  )
}
  • Теперь сопоставьте задачи и покажите данные пользователю. Он будет показывать «загрузка…», пока компоненты не получат данные из API.
import { useEffect } from "react";
export default function Todo() {
  const [todos, setTodos] = useState([])
  //callBackFunction to fetch todods
  function fetchTodos(){
    fetch('https://jsonplaceholder.typicode.com/todos')
    .then(response => response.json())
    .then(json => setTodos(json))
  }
  //useEffect(callBackFunction, [dependenciesArray]);
  useEffect(fetchTodos, []); 
  return (
    <div>
    <h1>Todo App</h1>
    {!todos.length && <h1>loading...</h1>}
    <ul>
      {todos?.map(({id, title}) => (
        <li key={id}>{title}</li>
      ))}
    </ul>
    </div>
  )
}

What are hooks in React

Кэшируйте свои данные с помощью useMemo и useCallback Hooks

Возможно, вы уже знаете, что при изменении состояния компонент выполняет повторную визуализацию, что гарантирует актуальность пользовательского интерфейса. Однако повторная отрисовка компонентов может оказаться ресурсоемкой и повлиять на общую производительность вашего приложения.

В качестве примера предположим, что в компоненте вашего приложения есть кнопка, открывающая модальное окно, а также выполняются тяжелые вычисления. Когда вы открываете или закрываете модальное окно, вам может не потребоваться выполнять сложные вычисления.

Итак, чтобы решить эту проблему, вы можете кэшировать или запоминать данные, требующие ресурсоемких операций, и в React вы можете сделать это с помощью перехватчиков useMemo и useCallback. Давайте разберемся с ними обоими по отдельности.

useMemo – запоминание для кэширования значений

useMemo Hook кэширует вычисленное значение операции всякий раз, когда изменяется состояние или происходит повторный рендеринг.

Синтаксис использования перехватчика useMemo:

const cachedValue = useMemo(calculateValue, dependencies)

* calculateValue: функция, вычисляющая значение, которое вы хотите кэшировать. Это должна быть чистая функция, не принимающая никаких аргументов. * зависимости: от которых зависит CalculValue и будет вызываться при каждом изменении этих зависимостей. Это могут быть реквизиты, состояния или переменные или функции, объявленные внутри этого компонента.

Hooks

useCallback — мемоизация для кэширования функций

Хук useCallback — это еще одна функция, которая позволяет нам кэшировать данные в приложениях React. Он принимает функцию и ее массив зависимостей. Как и хук useMemo, функция запускается каждый раз при изменении зависимостей.

const cachedFunction = useCallback(function, [dependencies])

Давайте рассмотрим пример ниже:

В компоненте SearchProduct перехват useCallback отслеживает искомый элемент и гарантирует, что из массива будет получен правильный элемент.

Управление состояниями с помощью хука useReducer

Хук useReducer — еще один отличный крючок для управления состояниями в приложениях React. Однако, в отличие от перехватчика useState, он обычно используется в компонентах с многочисленными состояниями в нескольких обработчиках событий.

Хук useReducer состоит из четырех основных компонентов: состояние, функция reducer, действие и . >функция отправки.

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

Действие представляет собой объект, содержащий тип и свойство полезной нагрузки. Свойство type определяет точное действие, которое должна выполнить функция редуктора, а полезные данные могут принимать данные от пользователя или других частей приложения.

Давайте рассмотрим пример с использованием контактной формы, которая принимает имя, адрес электронной почты и сообщение от пользователей.

import { useReducer } from "react";
const ACTIONS = {
    UPDATE_NAME: "updateName",
    UPDATE_EMAIL: "updateEmail",
    UPDATE_MESSAGE: "updateMessage",
};
const reducer = (state, action) => {
    switch (action.type) {
        case ACTIONS.UPDATE_NAME:
            return { ...state, name: action.payload.value };
        case ACTIONS.UPDATE_EMAIL:
            return { ...state, email: action.payload.value };
        case ACTIONS.UPDATE_MESSAGE:
            return { ...state, message: action.payload.value };
        default:
            return state;
    }
};
export default function App() {
    const [state, dispatch] = useReducer(reducer, {
        name: "",
        email: "",
        message: "",
    });
    const handleSubmit = (e) => {
        e.preventDefault();
        console.log({ state });
    };
    return (
        <div>
            <h2>Contact Us</h2>
            <form onSubmit={handleSubmit}>
                <label>Full Name</label>
                <input
                    type='text'
                    value={state.name}
                    onChange={(e) => {
                        dispatch({
                            type: ACTIONS.UPDATE_NAME,
                            payload: {
                                value: e.target.value,
                            },
                        });
                    }}
                />
                <label>Email Address</label>
                <input
                    type='email'
                    value={state.email}
                    onChange={(e) => {
                        dispatch({
                            type: ACTIONS.UPDATE_EMAIL,
                            payload: {
                                value: e.target.value,
                            },
                        });
                    }}
                />
                <label>Message</label>
                <textarea
                    rows={6}
                    value={state.message}
                    onChange={(e) => {
                        dispatch({
                            type: ACTIONS.UPDATE_MESSAGE,
                            payload: {
                                value: e.target.value,
                            },
                        });
                    }}
                />
                <button type='submit'>SEND</button>
            </form>
        </div>
    );
}

Приведенный выше фрагмент кода принимает адрес электронной почты, имя пользователя и сообщение от пользователя и сохраняет их в состоянии с помощью перехватчика useReducer.

Позвольте мне объяснить лучше:

const [state, dispatch] = useReducer(reducer, {
        name: "",
        email: "",
        message: "",
});

При объявлении хука useReducer он принимает два параметра – функцию редуктора и объект состояния.

const ACTIONS = {
    UPDATE_NAME: "updateName",
    UPDATE_EMAIL: "updateEmail",
    UPDATE_MESSAGE: "updateMessage",
}
const reducer = (state, action) => {
    switch (action.type) {
        case ACTIONS.UPDATE_NAME:
            return { ...state, name: action.payload.value };
        case ACTIONS.UPDATE_EMAIL:
            return { ...state, email: action.payload.value };
        case ACTIONS.UPDATE_MESSAGE:
            return { ...state, message: action.payload.value };
        default:
            return state;
    }
};

Функция редуктора отвечает за обновление значений состояния внутри перехватчика useReducer. Он принимает состояние и действие в качестве параметров.

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

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

              <input                                 type='text'
                    value={state.name}
                    onChange={(e) => {
                        dispatch({
                            type: ACTIONS.UPDATE_NAME,
                            payload: {
                                value: e.target.value,
                            },
                        });
                    }}
                />

Понимание повторно используемых компонентов путем создания собственного крючка

Введение в пользовательские перехватчики

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

Например, пример: вам может понадобиться перехватчик, чтобы определить, находится ли пользователь в сети, проверить, работает ли ваше приложение в фоновом режиме, или для других специализированных задач.

Но почему и когда вам следует создавать собственный хук? Представьте, что у вас есть приложение, в котором один компонент выполняет задачу, нагружающую ЦП, и вы хотите уменьшить ее влияние, когда пользователь переключается на другую вкладку приложения. Тем временем другой компонент обрабатывает анимацию, и вы хотите остановить анимацию, когда вкладка находится за каким-либо другим приложением.

Custom hooks in React

Один из вариантов — написать код в каждом из этих компонентов для мониторинга их видимости, но более организованный и эффективный подход — создать собственный крючок для решения этой общей задачи. Таким образом, мы можем абстрагировать общие функции, делая наш код более понятным и простым в управлении, и это основная цель создания специального перехватчика.

Создание пользовательского крючка

Давайте создадим специальный хук usePageVisibility, чтобы проверять, видимо ли приложение.

Не забудьте использовать use перед именем любого создаваемого вами пользовательского перехватчика.

Для простоты мы написали usePageVisibility в том же компоненте; вы можете записать его в другую папку и использовать там, где вам нужно.

Давайте разберемся с хуком usePageVisibility:

import './App.css'
import { useState, useEffect } from "react";
// Custom hook to check if the tab is in the background
function usePageVisibility() {
  const [isVisible, setIsVisible] = useState(true);
  function handleVisibilityChange() {
    setIsVisible(document.visibilityState === 'visible');
  };
  useEffect(() => {
    document.addEventListener('visibilitychange', handleVisibilityChange);
    return function(){
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, []);
  return isVisible;
}
export default function App() {
  const isTabVisible = usePageVisibility();
  return (
    <div>
      <h1>Check Page Visibility</h1>
      <p>Is the tab visible? { isTabVisible ? 'Yes' : 'No' }</p>
    </div>
  );
}

* Он использует перехватчик useState для создания переменной состояния isVisible, которая показывает, видна ли страница в данный момент (по умолчанию это правда). * Функция handleVisibilityChange отвечает за обновление состояния isVisible на основе изменений состояния видимости страницы (видимой или скрытой). * Хук useEffect используется для: * Добавьте прослушиватель событий «visibilitychange» в документе. Это событие срабатывает при изменении состояния видимости страницы. * Внутри useEffect возвращается функция очистки. Эта функция удаляет прослушиватель событий из компонента с помощью перехватчика при изменении зависимостей. * Перехватчик возвращает состояние isVisible, предоставляя логическое значение, указывающее, видна ли веб-страница пользователю в данный момент или нет.

<блок-цитата>

Мы вызываем хук usePageVisibility в App, и он присваивается isTabVisible, значение isTabVisible будет либо true, либо false. На основе этой переменной мы печатаем «Да» или «Нет» в пользовательском интерфейсе.

React-Custom-Hooks

Распространенные ошибки, которых следует избегать

Эффективное использование перехватчиков React может значительно улучшить разработку приложений React. Однако существуют распространенные ошибки и ловушки, которые вы допускаете и которых можно избежать. предотвратить непредвиденные проблемы. Вот некоторые распространенные ошибки, на которые следует обратить внимание при использовании хуков React:

  • Всегда вызывайте перехватчики на верхнем уровне функционального компонента или в других пользовательских перехватчиках. Избегайте вызова перехватчиков внутри циклов, условий или вложенных функций.

export default function App(){
  // Call your hooks here
}
  • При использовании useEffect обязательно указывайте зависимости в качестве второго аргумента. Отсутствие зависимостей может привести к неожиданному поведению.
useEffect(() => {
  // Missing dependencies can lead to bugs
}, []);
  • Будьте осторожны при использовании useState внутри useEffect, поскольку при неправильном управлении это может привести к бесконечным циклам рендеринга.
useEffect(() => {
  // Causes an infinite render loop without dependency management
  setState(state + 1);
}, []);
  • Не изменяйте переменные состояния напрямую. Всегда используйте функцию обновления состояния, возвращаемую useState, для обновления состояния.
state.property = 'new value'; // Incorrect, don't modify state directly
  • При необходимости выполните рефакторинг общей логики в специальные перехватчики. Невыполнение этого требования может привести к дублированию кода и снижению удобства сопровождения.
const MyComponent = () => {
  // Common logic duplicated in multiple components
  useEffect(() => {
    // Common logic
  });
};

Вывод

Хуки React изменили способ управления состоянием и побочными эффектами в функциональных компонентах. Это простые функции JavaScript, которые упрощают и улучшают разработку приложений React.

Вот краткий обзор ключевых моментов, которые мы рассмотрели в этом посте:

* Перехватчики React — одна из важнейших концепций React, и понимание ее поможет вам эффективно создавать приложения. * В этом посте мы рассмотрели два наиболее часто используемых хука: useState и useEffect. * useState позволяет нам управлять состоянием компонента, которое представляет собой конкретную память компонента, которая меняется со временем по мере взаимодействия пользователей с нашим приложением. * Хук useEffect используется для управления и отслеживания различных действий внутри ваших компонентов, таких как вызовы API и мониторинг, когда компонент монтируется и отключается в приложении. * Хук useReducer используется для управления состояниями внутри приложений со сложным управлением состояниями и переходами. * Вы также узнали, как создавать собственные перехватчики для определенных задач, например проверки того, находится ли вкладка веб-приложения в фоновом режиме. * Пользовательские перехватчики помогают нам повторно использовать логику в разных компонентах. * Мы также узнали, как избежать некоторых распространенных ошибок, с которыми могут столкнуться многие новички, например: несоблюдение правил перехватов, игнорирование зависимостей в массиве зависимостей, возникновение бесконечных циклов рендеринга, неправильное использование функций обновления состояния и многое другое.

Надеюсь, этот пост окажется полезным, и вы узнали, как использовать хуки в своем приложении React.

Если вы хотите узнать больше о ReactJS, начните читать некоторые из моих последних статей.

Любопытство не просто взяло верх над котом — оно привело вас сюда! Если эта статья вызвала у вас интерес, представьте, что еще вас ждет.

Побродите в чудо; ваше следующее техническое сокровище ждет в моей коллекции HackerNoon.


Также опубликовано здесь.


Оригинал