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

Вот несколько вопросов:

  • Зачем использовать два метода для одной цели?

  • Почему бы просто не вернуть данные или JXS из useCallback и использовать их?

  • Какая разница между этими двумя методами?

Разница в месте вызова

useMemo вызывается исходниками React'а, а useCallback - мы. Примеры обоих функций:

function memoUsed() {
  const _  = useMemo(() => {
    return ‘insert JSX here’
  })

  return _
}

function callbackUsed() {
  const _  = useCallback(() => {
    return ‘insert JSX here’
  })

  return _()
}

Вуаля, функция useMemo имитирована очень даже прилично, с помощью вызова коллбека (6 и 14 строки). Одна вещь сделано двумя не много разными способами. Дело в том, что в “память” компоненты при пользовании useMemo попадает значение только из return, игнорируя остальное тело функции. В случае с useCallback - коллбек передаётся как строка, без вызова. Но эта разница не влияет на количество маунтов или рендеров компоненты, записи в плане производительности эквивалентны.

В useMemo не рекомендуется использовать другие хуки

Примерчик:

function memoUsed() {
  const [state, setState] = useState(null)

  const _  = useMemo(() => {
    // причинит бесконечный ре-рендер
    setState(‘value’)
    return ‘insert JSX here’
  })

  return _
}

function callbackUsed() {
  const [state, setState] = useState(null)

  const _  = useCallback(() => {
    // обычное дело
    setState(‘value’)
    return ‘insert JSX here’
  })

  return _()
}

При вызове постороннего хука в useMemo и сопутствующем изменении состояния компонента приведёт к зацикливанию. Так как useMemo отрабатывает при стадии рендеринга, модификация состояния компонента запустит процесс заново. С useCallback таких проблем нет, функция вызывается по пользовательскому событию.

В useMemo аргумент-функция не принимает параметры

Ближе к делу:

function memoUsed() {
  const _  = useMemo((arg1) => {
    // React игнорирует аргументы
    return ‘insert JSX here’
  }, [])

  return _
}

function callbackUsed() {
  const _  = useCallback((what, where) => {
    // могут быть использованы в функции
    return ‘insert ${what} ${where}’
  })

  return _(‘JSX’, ‘here’)
}

По документации для useCallback массив зависимостей обязателен

А для useMemo может являться и undefined, что не совсем точно. При отсутствии массива зависимостей в обоих хуках происходит ре-рендер в ста процентах случаев. Это описание является ошибкой, про что заявлено в комментарии в исходном коде.

// allow undefined, but don't make it optional as that is very likely a mistake

В целом, хук useMemo можно заменить судя по всему и существует лишь для абстрактного отделения мемоизации данных от мемоизации методов. Кто знает, может в дальнейших версиях React'a способы хранения данных будут изменяться, так как это очень похоже на наследие классовых компонентов.

Комментарии (2)


  1. diomas
    23.09.2021 01:24
    +4

    что-то слишком многословно, `useCallback(fn, deps)` это просто `useMemo(() => fn, deps)`


    1. Gary_Ihar
      23.09.2021 09:45
      +1

      Краткий гайд "как обесценить статью" :)