useCallback необходим в React.memo? - PullRequest
12 голосов
/ 09 июля 2020

Рассмотрим этот пример:

import React, { useCallback } from 'react';

type UserInputProps = {
  onChange: (value: string) => void;
};

const UserInput = React.memo(({ onChange }: UserInputProps) => {
  // Is this `useCallback` redundant?
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" onChange={handleChange} />;
});

export default UserInput;

Мои вопросы:

  1. Когда реквизит состоит только из onChange и не содержит других элементов, не требуется ли useCallback в в этом случае, поскольку весь компонент уже запомнен на основе onChange?
  2. Если мы добавим дополнительную опору (скажем, value для начального значения для <input>), тогда я думаю useCallback становится полезным, поскольку в противном случае handleChange будет воссоздан, даже если onChange не изменится, а value изменится. Это правильно?

Ответы [ 2 ]

8 голосов
/ 09 июля 2020

Когда реквизит содержит только onChange и никаких других элементов, не требуется ли useCallback в этом случае, поскольку весь компонент уже запомнен на основе onChange?

Запоминание основано на всех реквизитах, а не только на onChange. Да, useCallback здесь не нужен.

Если мы добавим дополнительную опору (скажем, value для начального значения для <input>), то я думаю, что useCallback станет полезным, поскольку в противном случае handleChange будет воссоздан, даже если onChange не изменится, но значение изменится. Это правильно?

Если вы хотите обновить value, а не onChange при обновлении input, вы должны добавить value к своим реквизитам и продолжать использовать useCallback для вашего handleChange (если хотите, чтобы React не заменял новый обработчик событий при установке значения; у меня такое впечатление, что это часто излишне). Например:

const UserInput = React.memo(({ onChange, value }: UserInputProps) => {
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" value={value} onChange={handleChange} />;
});

Это будет использовать последнюю value и повторно использовать предыдущий handleChange, если свойство onChange не изменилось.

Вы можете не оставить React.memo в этом случае, если вы ожидаете, что value будет изменено в результате onChange. То есть, если вы ожидаете, что в общем случае value будет отличаться каждый раз при вызове вашего компонента, тогда проверка свойств React.memo будет дополнительной работой, которую вы можете не выполнять:

const UserInput = ({ onChange, value }: UserInputProps) => {
  const handleChange = useCallback(
    (event) => {
      onChange(event.target.value);
    },
    [onChange]
  );

  return <input type="text" value={value} onChange={handleChange} />;
};

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

Если вы просто хотите чтобы предоставить компоненту начальное значение, а затем управлять им локально, вы можете продолжать использовать React.memo (поскольку он по-прежнему отображает то же самое для тех же входных реквизитов), в этом случае вы этого не сделаете. need useCallback:

const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
  const [value, setValue] = useState(initialValue);
  const handleChange = (event) => {
    setValue(event.target.value);
    onChange(event.target.value);
  };

  return <input type="text" value={value} onChange={handleChange} />;
});

Это будет вызвано только при изменении onChange или initialValue. Вы также можете использовать здесь useCallback, чтобы, опять же, обновлять onChange на input при изменении свойства onChange, чтобы React не удалял старый обработчик и не устанавливал новый, когда только value изменения:

const UserInput = React.memo(({ onChange, initialValue }: UserInputProps) => {
  const [value, setValue] = useState(initialValue);
  const handleChange = useCallback(
    (event) => {
      setValue(event.target.value);
      onChange(event.target.value);
    },
    [onChange]
 );

  return <input type="text" value={value} onChange={handleChange} />;
});

Примечание: нужно помнить, что новая функция handleChange создается каждый раз, когда вызывается функция вашего компонента, даже если вы используете useCallback. Он должен быть таким, чтобы его можно было передать в useCallback в качестве аргумента. Единственная разница заключается в том, используете ли вы эту новую функцию или исходную, которая была создана в первый раз (результат useCallback). Я думаю, что причиной повторного использования первого, созданного для данного набора зависимостей, является минимизация изменений, передаваемых дочерним компонентам.

2 голосов
/ 09 июля 2020

Когда свойства состоят только из onChange и не содержат других элементов, является useCallback ненужным в этом случае , поскольку весь компонент уже запомнен на основе onChange?

Нет, это может быть необходимо , memo и useCallback не служат той же цели.

Без useCallback у вас может быть " тяжелое вычисление »при каждом рендеринге:

« тяжелое вычисление »относится к общему c случаю, а не к этому конкретному c примеру, где вы передаете только значение события.

const UserInput = React.memo(({ onChange = () => {} }) => {
  // Uncomment for memoization
  // Note that you can implement useCallback with useMemo

  // const handleChange = useMemo(() => {
  //   console.log("heavy computation memoized");
  //   return event => {
  //     onChange(event.target.value);
  //   };
  // }, [onChange]);

  const handleChange = event => {
    // Here we can have some heavy computation
    // Not related to this specific usecase
    console.log("heavy computation on every render");
    onChange(event.target.value);
  };

  return <input type="text" onChange={handleChange} />;
});

Если мы добавим дополнительную опору (скажем, значение для начального значения для), то я думаю, что useCallback станет полезным, поскольку в противном случае handleChange будет воссоздан, даже если onChange не t изменяется, но значение изменяется. Это правильно?

Если вы собираетесь использовать управляемый компонент (из-за использования value prop из input), , initialValue будет инициализирован один раз, поэтому бесполезно пытаться его запомнить (с какой целью?):

const UserInputWithValue = ({ onChange, initialValue }) => {
  // initilized once, no need to memoize
  const [value,setValue] = useState(initialValue);
  const handleChange = useCallback(
    (event) => {
      setValue(event.target.value)
      onChange(event.target.value);
    },
    [onChange]
  );

  // Controlled
  return <input type="text" value={value} onChange={handleChange} />;
};

Редактировать объект-коричневый-нюй c

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