JavaRush /Курсы /Модуль 3: React /Типизация значений в useMemo и управление зависимостями

Типизация значений в useMemo и управление зависимостями

Модуль 3: React
3 уровень , 6 лекция
Открыта

Типизация значений, возвращаемых useMemo

Типизация в useMemo проста: TypeScript автоматически выводит тип, если он явно возвращается из вычисляемой функции.

const multiplier: number = 3;

const value = useMemo(() => {
  return multiplier * 1000;
}, [multiplier]); // TypeScript сам выводит тип как number

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

const cachedUsers = useMemo<{
  id: string;
  name: string;
}[]>(() => {
  return fetchUsers().filter((user) => user.isActive);
}, [dependency]);

Здесь <{ id: string; name: string; }[]> гарантирует, что результат будет строго массивом объектов с заданной структурой.

Использование интерфейсов для типизации

Масштабируемость и читаемость кода — наше всё. Вместо того, чтобы вручную описывать типы, создавайте интерфейсы:

interface User {
  id: string;
  name: string;
  isActive: boolean;
}

const activeUsers = useMemo<User[]>(() => {
  return fetchUsers().filter((user) => user.isActive);
}, [dependency]);

Это особенно полезно, если типы используются в нескольких местах. Почему бы не дать TypeScript почувствовать себя важным?

Управление зависимостями в useMemo

Зависимости в useMemo — это массив, содержащий переменные, которые влияют на работу вычисляемой функции.

Пример:

const memoizedValue = useMemo(() => {
  return someValue * multiplier;
}, [someValue, multiplier]); // зависимости

Если someValue или multiplier изменяются, функция внутри useMemo вызывается заново. Здесь нужно быть внимательным, иначе можно или пропустить изменения (не добавив переменную в зависимости), или вызвать лишние рендеры (добавив что-то лишнее).

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

Пропуск зависимости

Если вы забыли добавить переменную в зависимости, мемоизированное значение не обновится при её изменении.

const memoizedValue = useMemo(() => {
  return someValue * multiplier;
}, [multiplier]); // где someValue? ошибка!

TypeScript не может автоматически определить, что вы забыли добавить someValue. Поэтому включите строгий режим ESLint для хуков: react-hooks/exhaustive-deps.

Лишние зависимости

Если добавить что-то ненужное, вы будете пересчитывать мемоизированное значение впустую.

const memoizedValue = useMemo(() => {
  return someValue * multiplier;
}, [someValue, multiplier, array]); // array не нужен!

В данном случае array — лишняя зависимость, если она не влияет на вычисляемое значение.

Как правильно управлять зависимостями

Придерживайтесь принципов:

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

Использование мемоизации для зависимостей

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

const memoizedArray = useMemo(() => [1, 2, 3], []); // мемоизируем массив

const memoizedValue = useMemo(() => {
  return someValue * multiplier * memoizedArray.length;
}, [someValue, multiplier, memoizedArray]); // OK

Подводные камни зависимостей и их решения

1. Ошибка "react-hooks/exhaustive-deps"

Иногда TypeScript (или ESLint) может выдавать ошибку, требуя добавить ненужные зависимости. Например:

const memoizedValue = useMemo(() => {
  return someValue * multiplier;
}, [multiplier]); // ESLint требует добавить someValue

В случае уверенности, что переменная не влияет на результат, вы можете отключить проверку:

// eslint-disable-next-line react-hooks/exhaustive-deps
const memoizedValue = useMemo(() => {
  return someValue * multiplier;
}, [multiplier]);

Но используйте это осторожно!

2. Работа с функциями-зависимостями

Если зависимостью является функция (или, что ещё хуже, стрелочная!), создавайте её с помощью useCallback.

const handleClick = useCallback(() => {
  console.log('Click!');
}, []); // мемоизация функции

const memoizedValue = useMemo(() => {
  return someValue * multiplier * handleClick.length; // OK
}, [someValue, multiplier, handleClick]);

Практический пример: оптимизация таблицы

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

import React, { useMemo } from 'react';

interface Row {
  id: string;
  name: string;
  age: number;
}

const Table: React.FC<{ rows: Row[]; filter: string }> = ({ rows, filter }) => {
  const filteredRows = useMemo(() => {
    console.log('Фильтрация...');
    return rows.filter((row) => row.name.includes(filter));
  }, [rows, filter]); // мемоизация на основе rows и filter

  return (
    <table>
      <tbody>
        {filteredRows.map((row) => (
          <tr key={row.id}>
            <td>{row.name}</td>
            <td>{row.age}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};
  1. Мы мемоизируем filteredRows, чтобы не пересчитывать их каждый раз.
  2. Зависимости строго соответствуют переменным, влияющим на результат.

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

1
Задача
Модуль 3: React, 3 уровень, 6 лекция
Недоступна
Мемоизация простого значения
Мемоизация простого значения
1
Задача
Модуль 3: React, 3 уровень, 6 лекция
Недоступна
Мемоизация массива данных
Мемоизация массива данных
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ