React Hooks: Есть ли причина, по которой отказ от create или, соответственно, обратного вызова для useEffect и useCallback - это плохо? - PullRequest
0 голосов
/ 24 апреля 2019

Итак, я читал «Полное руководство по использованию эффекта» Дана Абрамова и Документы по хукам .

В статье Дан приводит следующий пример:

function SearchResults() {
  const [query, setQuery] = useState('react');

  // ✅ Preserves identity until query changes
  const getFetchUrl = useCallback(() => {
    return 'https://hn.algolia.com/api/v1/search?query=' + query;
  }, [query]);  // ✅ Callback deps are OK

  useEffect(() => {
    const url = getFetchUrl();
    // ... Fetch data and do something ...
  }, [getFetchUrl]); // ✅ Effect deps are OK

  // ...
}

И документы Хука дают это:

function ProductPage({ productId }) {
  // ✅ Wrap with useCallback to avoid change on every render
  const fetchProduct = useCallback(() => {
    // ... Does something with productId ...
  }, [productId]); // ✅ All useCallback dependencies are specified

  return <ProductDetails fetchProduct={fetchProduct} />;
}

function ProductDetails({ fetchProduct })
  useEffect(() => {
    fetchProduct();
  }, [fetchProduct]); // ✅ All useEffect dependencies are specified
  // ...
}

Мне было интересно: действительно ли необходимы обратный вызов и функция фабрики?

Вы также можете написать:

const getFetchUrl = useCallback('https://hn.algolia.com/api/v1/search?query=' + query, [query]);

и

useEffect(fetchProduct, [fetchProduct]);

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

function Greeting({ name }) {
  const calculateExpensive = useCallback(() => {
    return `Hello ${name}`;
  }, [name]);

  const result = useMemo(calculateExpensive, [calculateExpensive]);
  return <p>{result}</p>;
}

Я уверен, что яЯ делаю глупую ошибку здесь.Что я не вижу и делаю неправильно?

1 Ответ

1 голос
/ 24 апреля 2019

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

useCallback должен принимать функцию обратного вызова в качестве аргумента, как следует из названия. Поскольку useCallback(fn) является сокращением для useMemo(() => fn), технически его можно (неправильно) использовать с любым аргументом:

const getFetchUrl = useCallback('https://hn.algolia.com/api/v1/search?query=' + query, [query]);

Нет никакой выгоды от этого, потому что useMemo и useCallback предназначены для ленивой оценки, а это приводит к нетерпеливой оценке.

Пример с обратным вызовом getFetchUrl не очень показателен, поскольку запоминание не дает никаких улучшений, его можно упростить до:

function SearchResults() {
  const [query, setQuery] = useState('react');

  const fetchUrl = 'https://hn.algolia.com/api/v1/search?query=' + query;

  useEffect(() => {
    // ... Fetch data and do something ...
  }, [fetchUrl]);
}

Что касается примера Greeting, useCallback является избыточным. Если вычисление действительно дорого и его нужно лениво оценивать (в данном примере это не так), для этого useMemo:

function Greeting({ name }) {
  const result = useMemo(() => {
    return `Hello ${name}`;
  }, [name]);

  return <p>{result}</p>;
}
...