Логирование состояния

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

Основы состояния в Haunted

Состояние в Haunted реализуется через хук useState. Создание состояния выглядит так:

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

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

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
    </div>
  `;
}

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

useState возвращает массив из текущего значения и функции для его изменения. Любое изменение значения автоматически перерисовывает компонент.

Логирование изменений состояния

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

import { useEffect } from 'haunted';

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

  useEffect(() => {
    console.log('Состояние count изменилось:', count);
  }, [count]);

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
    </div>
  `;
}

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

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

Создание обёртки для логирования

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

function useLoggedState(initialValue, name = 'state') {
  const [value, setValue] = useState(initialValue);

  useEffect(() => {
    console.log(`[LOG] ${name}:`, value);
  }, [value]);

  return [value, setValue];
}

Теперь можно использовать useLoggedState вместо useState, чтобы получать автоматическое логирование:

function Counter() {
  const [count, setCount] = useLoggedState(0, 'count');

  return html`
    <div>
      <p>Счётчик: ${count}</p>
      <button @click=${() => setCount(count + 1)}>Увеличить</button>
    </div>
  `;
}

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

Логирование сложного состояния

Если состояние представляет собой объект или массив, важно логировать копию состояния, чтобы избежать путаницы с мутациями:

function useLoggedObjectState(initialValue, name = 'state') {
  const [state, setState] = useState(initialValue);

  useEffect(() => {
    console.log(`[LOG] ${name}:`, JSON.parse(JSON.stringify(state)));
  }, [state]);

  return [state, setState];
}

Пример использования:

function TodoList() {
  const [todos, setTodos] = useLoggedObjectState([], 'todos');

  const addTodo = (text) => setTodos([...todos, { text, done: false }]);

  return html`
    <div>
      <ul>
        ${todos.map(todo => html`<li>${todo.text}</li>`)}
      </ul>
      <button @click=${() => addTodo('Новое задание')}>Добавить</button>
    </div>
  `;
}

customElements.define('my-todo', component(TodoList));

Интеграция с внешними логерами

Для крупных приложений логирование в консоль может быть недостаточно. Haunted позволяет интегрироваться с любыми системами логирования, например loglevel или собственными сервисами:

import log from 'loglevel';

function useLoggedStateExternal(initialValue, name = 'state') {
  const [value, setValue] = useState(initialValue);

  useEffect(() => {
    log.info(`[LOG] ${name}:`, value);
  }, [value]);

  return [value, setValue];
}

Такой подход позволяет централизовать сбор информации о состоянии компонентов, хранить логи на сервере или интегрировать с аналитикой.

Полезные рекомендации

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

Эти техники позволяют создать прозрачное и контролируемое управление состоянием компонентов Haunted, ускоряют отладку и повышают надежность интерфейсов.