Массив зависимостей

Haunted — это минималистичная библиотека для создания веб-компонентов на базе функциональных компонентов и реактивных хуков, вдохновлённая React, но использующая нативные Web Components. Одной из ключевых возможностей Haunted является управление состоянием и побочными эффектами через хуки, аналогичные React Hooks, включая useEffect и массив зависимостей.

Хук useEffect и его синтаксис

useEffect позволяет выполнять побочные эффекты после рендера компонента, таких как работа с DOM, асинхронные запросы или установка таймеров. Базовый синтаксис:

import { useEffect } from 'haunted';

useEffect(() => {
  // Логика побочного эффекта
  console.log('Компонент отрендерен');

  return () => {
    // Очистка эффекта
    console.log('Компонент размонтирован');
  };
}, [/* массив зависимостей */]);

Ключевой элемент — массив зависимостей (dependency array). Он определяет, когда эффект должен повторно выполняться. Если массив пустой [], эффект выполняется только один раз после первого рендера. Если массив содержит переменные, эффект будет срабатывать каждый раз при изменении любой из них.

Значение массива зависимостей

Массив зависимостей выполняет две задачи:

  1. Оптимизация производительности — позволяет избежать лишних вызовов эффектов при каждом рендере.
  2. Контроль реактивности — задаёт условия, при которых эффект должен обновляться.

Пример с массивом зависимостей:

import { html, component, useState, useEffect } from 'haunted';

function Counter() {
  const [count, setCount] = useState(0);
  const [step, setStep] = useState(1);

  useEffect(() => {
    console.log(`Счётчик обновился: ${count}`);
  }, [count]); // Эффект срабатывает только при изменении count

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${() => setCount(count + step)}>Увеличить</button>
      <button @click=${() => setStep(step + 1)}>Изменить шаг (${step})</button>
    </div>
  `;
}

customElements.define('my-counter', component(Counter));

В этом примере эффект выводит сообщение в консоль только при изменении count, игнорируя изменения step. Это демонстрирует точечное реагирование на изменения состояния через массив зависимостей.

Массив зависимостей и сложные объекты

Массив зависимостей сравнивает элементы по ссылке, а не по содержимому. Если в массиве находится объект, массив или функция, эффект будет выполняться при каждой генерации нового объекта, даже если его содержимое не изменилось.

useEffect(() => {
  console.log('Объект изменился');
}, [{ value: 1 }]); // Всегда сработает, так как объект создаётся заново при каждом рендере

Чтобы избежать лишних вызовов, объекты нужно хранить в состоянии или мемоизировать через useState или useMemo.

Очистка эффектов и массив зависимостей

Функция очистки, возвращаемая из useEffect, срабатывает перед повторным вызовом эффекта и при размонтировании компонента. Это важно при работе с таймерами, подписками и сетевыми соединениями:

useEffect(() => {
  const timer = setInterval(() => console.log(count), 1000);
  return () => clearInterval(timer);
}, [count]); // Каждый раз при изменении count старый таймер очищается

Если массив зависимостей пустой, функция очистки выполняется только при размонтировании компонента.

Особенности поведения массива зависимостей

  • Пустой массив [] — эффект выполняется один раз после первого рендера.
  • Неуказанный массив — эффект выполняется после каждого рендера.
  • Массив с переменными — эффект выполняется только при изменении хотя бы одной из переменных.

Неправильное указание зависимостей может привести к багам или лишним рендерам. Например, использование переменной внутри эффекта без её добавления в массив зависимостей создаёт непредсказуемое поведение.

Рекомендации по использованию

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

Пример с асинхронными запросами

useEffect(() => {
  let isMounted = true;

  async function fetchData() {
    const response = await fetch('https://api.example.com/data');
    const data = await response.json();
    if (isMounted) {
      setData(data);
    }
  }

  fetchData();

  return () => {
    isMounted = false; // предотвращает обновление состояния после размонтирования
  };
}, [url]); // Эффект срабатывает при изменении url

Массив зависимостей гарантирует, что новый запрос выполняется только при изменении URL, предотвращая лишние запросы и потенциальные утечки памяти.

Итоговые принципы

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

Эффективное использование массива зависимостей делает Haunted мощным инструментом для управления состоянием и побочными эффектами в веб-компонентах.