Примечание. Прежде чем приступить к работе с блогом, важно иметь четкое представление об основных принципах и концепциях, связанных с хуками.
Поскольку размер и сложность кода продолжают расти, важность написания эффективного кода React становится все более первостепенной. Расходы, связанные с повторным рендерингом существенных компонентов и возложением большой нагрузки на браузер с помощью одностраничных приложений (SPA), могут привести к увеличению времени обработки и потенциально отпугнуть пользователей.
Реагировать на оптимизацию производительности
React решает эти проблемы, предоставляя ряд встроенных функций API, предназначенных для повышения производительности приложений, таких как предотвращение ненужного повторного рендеринга, кэширование повторяющихся и ресурсоемких операций, а также включение ленивой загрузки компонентов среди других оптимизаций.
В этом руководстве основное внимание уделяется изучению и демонстрации использования двух разных хуков React, а именноuseMemo
иuseCallback
. Кроме того, хуки позволяют разработчикам упростить управление состоянием, оптимизировать повторную визуализацию и повысить общую эффективность своих приложений React.
Что такое useMemo?
В некоторых сценариях возникает необходимость включить сложные вычисления в наши компоненты React, что, хотя и неизбежно, может привести к небольшому замедлению процесса рендеринга. Внедрив механизм кэширования, можно эффективно устранить вышеупомянутую проблему снижения производительности, гарантируя, что вычисления будут выполняться только при необходимости.
Используя useMemo
, React может оптимизировать производительность функциональных компонентов, запоминая результат функции и пересчитывая его только при изменении зависимостей в массиве.
Практический пример без useMemo:
import React, { useState } from "react"; const Counter = () => { const [countOne, setCountOne] = useState(0); const [countTwo, setCountTwo] = useState(0); const incrementCountOne = () => { setCountOne(countOne + 1); }; const incrementCountTwo = () => { setCountTwo(countTwo + 1); }; const multipleCountOne = () => { let i = 0; let res = 1; /* Perform some heavy operation */ while (i < 2000000000) { i++; } return res * countOne; }; return ( <> <button onClick={incrementCountOne}>Counter - {countOne}</button> <button onClick={incrementCountTwo}>Counter - {countTwo}</button> <br /> <span>{multipleCountOne()}</span> </> ); }; export default Counter;
В приведенном примере, несмотря на то, что тяжелая операция явно выполняется над CountOne
, любые изменения в CountOne
вызовут повторную визуализацию компонента, что также повлияет на визуализацию CountTwo
.
Практический пример с useMemo:
import React, { useState, useMemo } from "react"; const Memo = () => { const [countOne, setCountOne] = useState(0); const [countTwo, setCountTwo] = useState(0); const incrementCountOne = () => { setCountOne(countOne + 1); }; const incrementCountTwo = () => { setCountTwo(countTwo + 1); }; const multipleCountOne = useMemo(() => { let i = 0; let res = 1; /* Perform some heavy operation */ while (i < 2000000000) { i++; } return res * countOne; }, [countOne]); return ( <> <button onClick={incrementCountOne}>Counter - {countOne}</button> <button onClick={incrementCountTwo}>Counter - {countTwo}</button> <br /> <span>{multipleCountOne}</span> </> ); }; export default Memo;
В представленном примере с использованием useMemo
тяжелая операция над CountOne
может быть запомнена, гарантируя, что вычисление будет выполняться повторно только при изменении CountOne
. Этот механизм запоминания предотвращает любой ненужный повторный рендеринг компонента и не влияет на рендеринг CountTwo
.
Что такое useCallback?
Во время процесса повторного рендеринга в React всякий раз, когда компонент повторно визуализируется, ссылки на функции внутри компонента создаются заново. Это означает, что если у вас есть функция обратного вызова, переданная мемоизированному дочернему компоненту через реквизиты, она может быть повторно отрисована, даже если родительский компонент не изменил реквизиты дочернего компонента.
Используя хук useCallback в React, мы можем создавать эффективные обратные вызовы, которые избегают ненужного повторного рендеринга, кэшируя ссылку на функцию, когда зависимости остаются прежними.
Практический пример без useCallback:
//Parent.jsx import React, { useState } from "react"; import Child from "./Child"; const Parent = () => { const [countOne, setCountOne] = useState(0); const [countTwo, setCountTwo] = useState([]); const incrementCountOne = () => { setCountOne(countOne + 1); }; const printHelloWhenCountTwoChanges = () => { console.log("Hello count two"); }; return ( <> <Child countTwo={countTwo} printHello={printHelloWhenCountTwoChanges} /> <button onClick={incrementCountOne}>Counter - {countOne}</button> </> ); }; export default Parent; //Child.jsx import React, { memo } from "react"; const Child = ({ count, printHelloWhenCountTwoChanges }) => { console.log("Child component rendered"); return <div>Child</div>; }; export default memo(Child);
В данном примере дочерний компонент явно не зависит от состояния «countOne» родительского компонента, он по-прежнему подлежит повторному выполнению при нажатии кнопки. Несмотря на использование методов мемоизации, таких как React’s memo
, для предотвращения ненужного выполнения дочернего компонента, он продолжает ненужно отображать из-за повторяющегося вызова метода incrementCountTwo
каждый раз при загрузке родительского компонента.
Практический пример с useCallback:
//Parent.jsx import React, { useState } from "react"; import Child from "./Child"; const Parent = () => { const [countOne, setCountOne] = useState(0); const [countTwo, setCountTwo] = useState([]); const incrementCountOne = () => { setCountOne(countOne + 1); }; const printHelloWhenCountTwoChanges = useCallback(() => { console.log("Hello count two"); }, [countTwo]); return ( <> <Child countTwo={countTwo} printHello={printHelloWhenCountTwoChanges} /> <button onClick={incrementCountOne}>Counter - {countOne}</button> </> ); }; export default Parent; //Child.jsx import React, { memo } from "react"; const Child = ({ count, printHelloWhenCountTwoChanges }) => { console.log("Child component rendered"); return <div>Child</div>; }; export default memo(Child);
В приведенном выше примере очевидно, что дочерний компонент отображается только один раз из-за использования useCallback. Это эффективно предотвращает повторяющийся повторный рендеринг, вызванный повторяющимся вызовом метода incrementCountTwo
каждый раз при загрузке родительского компонента, что приводит к повышению производительности и эффективности.
Заключение
Подводя итог, при сравнении useCallback и useMemo их основное различие заключается в возвращаемых результатах: useCallback предоставляет запоминаемую функцию обратного вызова, а useMemo предлагает запоминаемое значение.
Применяя эти методы, вы сможете оптимизировать производительность вашего приложения и обеспечить лучший пользовательский интерфейс.
Спасибо, что прочитали этот урок! Я надеюсь, что вы нашли его полезным и что он даст вам знания для создания высокопроизводительных приложений с использованием React.