Реагировать на ненужный рендер элемента
Я только начал изучать React и собирал небольшое приложение, которое обращается к API котировок. У API есть конечная точка, которая возвращает случайную цитату. Когда приложение изначально загружается, оно обращается к API и показывает цитату, и есть кнопка, которую можно нажать, чтобы получить новую случайную цитату (новый вызов API).
У меня есть корневой компонент с именем App. Этот компонент имеет дочерний компонент QuoteWrap. Компонент QuoteWrap имеет два дочерних элемента: кнопку, которая используется для получения новой случайной цитаты, и компонент Quote, который показывает автора цитаты и саму цитату. Это код внутри компонента QuoteWrap:
export default function QuoteWrap() {
const { quoteData, isLoading, fireNewCall } = useQuote();
const handleClick = () => {
fireNewCall();
};
return(
<>
<button onClick={handleClick}>Get random quote</button>
{ isLoading ?
<h2>Loading...</h2>
:
<Quote author={quoteData.author} quote={quoteData.quote} />
}
</>
);
}
useQuote () - это настраиваемая ловушка, которая управляет вызовами API и возвращает 3 значения: 1 - данные, 2 - если вызов находится в процессе и 3 - функция для вызова API.
Очевидно, что каждый раз, когда нажимается кнопка, весь компонент QuoteWrap повторно визуализируется (при изменении quoteData и isLoading). Но на самом деле кнопку не нужно перерисовывать, так как она никогда не меняется.
Итак, я подумал: хорошо, я могу переместить кнопку в компонент приложения. Но тогда у меня нет доступа к функции fireNewCall в хуке useQuote.
Как я могу предотвратить повторную отрисовку кнопки? Это вообще важно в данном случае, или я слишком одержим повторным рендерингом React?
Спасибо!
Ответы
Ваш компонент будет повторно отрисовываться каждый раз, когда изменяется функция handleClick, то есть каждый раз при отрисовке QuoteWrap.
Решение - хук useCallback. useCallback будет возвращать ту же функцию для handleClick каждый раз при отрисовке QuoteWrap, если зависимости не изменились.
https://reactjs.org/docs/hooks-reference.html#usecallback
Вы бы использовали это так:
const handleClick = useCallback(() => {
fireNewCall();
},[fireNewCall]);
fireNewCall - это зависимость, поэтому, пока useQuote возвращает стабильную функцию fireNewCall, ваша кнопка не будет повторно отображаться, поскольку свойство handleClick не изменилось.
Я думаю, вы можете быть слишком одержимы повторными рендерингами React. Кнопка должна быть отрисована повторно, потому что в некоторых случаях handleClickее следует изменить fireNewCall. Даже если, handleClickникогда не изменится. Не нужно думать о повторном рендеринге элемента.
Примерно то, что сказали Бенджамин и Вьет - в исходном коде для handleClick при каждом рендеринге назначается новая функция. Вы можете использовать React.useCallbackисходную ссылку на функцию и обновлять ее только при изменении чего-либо в массиве зависимостей - в этом случае просто fireNewCallнужно перейти в массив зависимостей.
Но, как говорит Вьет, не слишком зацикливайтесь на этом. Использование React.useCallbackможет даже замедлить ваш код. Ознакомьтесь с сообщениями Kent C. Dodds When to useMemo и useCallback для получения дополнительной информации.