Типичные ошибки

Большинство проблем с timeago.js повторяются и хорошо известны. Знание паттернов ошибок сокращает время отладки и помогает правильно выстраивать архитектуру с самого начала.


Ошибка 1: Не вызывать cancel при размонтировании

// Плохо — таймеры накапливаются
useEffect(() => {
  render(ref.current, 'ru');
  // Нет возврата cleanup-функции
}, []);
// Правильно
useEffect(() => {
  if (!ref.current) return;
  render(ref.current, 'ru');
  return () => cancel(ref.current); // Обязательно
}, []);

Симптомы: замедление страницы со временем, рост потребления памяти, HTMLTimeElement в heap snapshot не удаляется.


Ошибка 2: Повторный render без cancel

// Плохо — двойные таймеры
function updateLocale(newLocale) {
  render(nodes, newLocale); // Добавляет новые таймеры к существующим
}
// Правильно
function updateLocale(newLocale) {
  cancel(nodes);
  render(nodes, newLocale);
}

Ошибка 3: Вызов render на сервере

// Плохо — ReferenceError: document is not defined
useEffect(() => {}, []); // Пропустил — render вызывается вне useEffect

render(el, 'ru'); // Топ-уровень компонента — выполняется на сервере
// Правильно — только в useEffect (клиент)
useEffect(() => {
  render(el, 'ru');
  return () => cancel(el);
}, []);

Ошибка 4: Передача null в format

const date = post.createdAt ?? null;
format(date, 'ru'); // Неверный результат или исключение
if (date) format(date, 'ru');
// Или
format(date ?? new Date(), 'ru');

Ошибка 5: Неверный формат строки даты

format('01.06.2025', 'ru');    // Неверный формат — поведение непредсказуемо
format('June 1, 2025', 'ru'); // Зависит от движка JavaScript
// Правильно — ISO 8601
format('2025-06-01T12:00:00Z', 'ru');
format('2025-06-01', 'ru');

Ошибка 6: Регистрировать локаль при каждом рендере

function TimeAgo({ date }) {
  register('ru', ruLocale); // Вызывается при каждом рендере компонента
  return <time>{format(date, 'ru')}</time>;
}
// Правильно — один раз в точке входа
import { register } from 'timeago.js';
import ru from 'timeago.js/esm/lang/ru';
register('ru', ru);

Ошибка 7: Несоответствие временных зон

// Сервер возвращает строку без Z — интерпретируется как локальное время
format('2025-06-01T12:00:00', 'ru');

Строка без часового пояса парсится браузером в локальном времени. В UTC+3 это будет 2025-06-01T09:00:00Z, что даёт неверную разницу.

// Правильно — всегда указывать Z или +HH:MM
format('2025-06-01T12:00:00Z', 'ru');
// Или нормализовать на бэкенде

Ошибка 8: Использовать render для статичного списка

// Лишние ресурсы — таймеры для списка, который не обновляется
posts.forEach(post => render(timeEl, 'ru'));
// Правильно для статичного контента
posts.map(post => ({
  ...post,
  timeLabel: format(post.createdAt, 'ru'),
}));

Ошибка 9: Индекс локали вне диапазона 0-14

register('custom', (n, i) => {
  const forms = [/* только 10 элементов */];
  return forms[i]; // undefined для i >= 10
});
register('custom', (n, i) => {
  const forms: [string, string][] = new Array(15).fill(['давно', 'скоро']);
  return forms[i] ?? ['давно', 'скоро']; // Всегда возвращать кортеж
});

Ошибка 10: Передавать NodeList напрямую в cancel

const nodes = document.querySelectorAll('[datetime]');
cancel(nodes); // NodeList — не всегда поддерживается

// Надёжнее
cancel(Array.from(nodes));

Ошибка 11: Не проверять наличие ref

const ref = useRef<HTMLTimeElement>(null);

useEffect(() => {
  render(ref.current, 'ru'); // ref.current может быть null на первом рендере
}, []);
useEffect(() => {
  if (!ref.current) return;
  render(ref.current, 'ru');
  return () => { if (ref.current) cancel(ref.current); };
}, []);

Ошибка 12: Ожидать синхронного обновления DOM

render(el, 'ru');
console.log(el.textContent); // '' — DOM ещё не обновлён

timeago.js обновляет DOM асинхронно через внутренние таймеры. Для немедленного начального значения использовать format:

el.textContent = format(el.getAttribute('datetime'), 'ru'); // Синхронно
render(el, 'ru'); // Запустить автообновление

Таблица ошибок

Ошибка Симптом Решение
Нет cancel Утечка таймеров return () => cancel(el)
render без cancel при смене Двойные таймеры cancel → render
render на сервере ReferenceError Только в useEffect/onMounted
null в format NaN или исключение Проверка перед вызовом
Неверный формат даты Неверная разница Использовать ISO 8601
Регистрация в компоненте Лишние вызовы Регистрировать один раз
Строка без часового пояса Неверное время Добавлять Z или offset