В этом посте мы посмотрю на то, как мы можем улучшить производительность реактивных приложений, которые нужно сделать много компонентов на экране.
Обычно мы считаем использованием Пагинация
или Виртуализация
Для большинства наших приложений предоставить лучший пользовательский опыт, и это работает для большинства случаев использования, но что, если у нас есть случай, когда нам нужно сделать много компонентов на экране, не отказывайтесь от опыта пользователя и представление.
Для демонстрации я рассмотрел простое приложение, которое отображает 30K квадратов на экране, и мы обновляем счет, когда квадраты нажали. Я использую Реагировать 17.0.0
и функциональные компоненты с крючками.
Вот предварительный просмотр приложения. У этого есть Приложение
Компонент и A Квадрат
компонент. Существует заметное отставание на нажатии на квадраты.
Packblitz Предварительный просмотр Код Stackblitz
// App.jsx import React, { useState } from "react"; import Square from "./components/square/square"; const data = Array(30000) .fill() .map((val, index) => { return { id: index, key: `square-${index}` }; }); const App = () => { const [count, setCount] = useState(0); const [items, setItems] = useState(data); return (); }; export default App;Count: {count}
{items.map(({ key, id, clicked }) => ({ const newItems = [...items]; newItems[id].clicked = true; setCount(val => val + 1); setItems(newItems); }} /> ))}
// Square.jsx import React from "react"; import "./square.css"; const Square = ({ onClick, id, clicked }) => { return (onClick(id)} /> ); }; export default Square;
Давайте добавим консольные утверждения как к компонентам, чтобы проверить, не являются ли они без необходимости, а затем нажмите на один из квадратов. Мы видим Квадрат
Функция компонентов называется 30k раз.
Кроме того, мы можем видеть, что 600 мс
проводятся в повторном рендеринге пользовательского интерфейса на вкладку Profiler Tools Advant DEV. Запустите профилирование на странице Load -> Нажмите на любой квадрат -> остановить профилирование.
Нам нужно избегать повторного рендеринга Квадрат
компонент как ни один из реквизит
Ибо это меняется. Мы будем использовать Отреагировать. Мэмо
для этого.
Что реагирует. Мэмо?
Отреагировать. Мэмо
является компонентом более высокого порядка, который помогает пропустить повторное рендеринг Memoingize результат исходного визуализации. Отреагировать. Мэмо
Повторные рендеры только тогда, когда опора
изменения.
Примечание: Отреагировать. Мэмо
делает неглубокое сравнение. Для получения дополнительной связи мы можем пройти функцию сравнения, как показано ниже.
React.memo(Component, (prevProps, nextProps) => { // return true if the props are same, this will skip re-render // return false if the props have changed, will re-render });
Вот Квадрат
компонент с Реагировать. Мэмо
// Square component with React.memo import React from "react"; import "./square.css"; const Square = ({ onClick, id, clicked }) => { return (onClick(id)} /> ); }; export default React.memo(Square);
Теперь давайте попробуем снова профилировать с дополнительной настройкой, как показано ниже.
Мы еще не видим никакой разницы. Но когда мы парием на Квадрат
Компонент показывает OnClick
PROP изменился, что вызвало это повторно рендеринг. Это происходит, когда мы передаем новую функцию во время каждого визуализации для OnClick
пропры Чтобы избежать этого мы используем UseCallback
Отказ
Что такое UseCallback?
UseCallback
это крючок, который возвращает воменутый обратный вызов.
// App component with useCallback import React, { useState, useCallback } from "react"; import Square from "./components/square/square"; const data = Array(30000) .fill() .map((val, index) => { return { id: index, key: `square-${index}` }; }); const App = () => { const [count, setCount] = useState(0); const [items, setItems] = useState(data); const onClick = useCallback( id => { const newItems = [...items]; newItems[id].clicked = true; setCount(val => val + 1); setItems(newItems); }, [items] ); return (); }; export default App;Count: {count}
{items.map(({ key, id, clicked }) => ())}
Давайте снова профиль. Теперь мы избегаем повторного рендеринга Квадраты
И это уменьшает время на 118 мс
Отказ
Теперь мы видим гораздо лучшую производительность. Мы избегаем повторного рендеринга Квадрат
Компоненты с использованием мемузаризации но Реагировать
Все еще необходимо сравнить реквизиты для всех элементов 30K. Вот компонентное дерево для нашего приложения.
Если вы все еще найдете проблемы с производительностью, мы можем пойти на один шаг вперед. У нас есть 30k Квадрат
Элементы ниже Приложение
компонент. Чтобы уменьшить реагирование времени, требуется для сравнения реквизитов, нам нужно уменьшить компоненты на этом уровне. Что можно сделать здесь? Можем ли мы представить еще один слой компонентов? Да, мы будем разделить список элементов 30K в меньшие куски и визуализируйте их, используя промежуточный компонент.
В приложении Real-World мы можем найти логическое место для разделения списка в меньшие куски. Но здесь давайте разделим их в куски 500 квадратов каждый.
// App component import React, { useState, useCallback } from "react"; import Row from "./components/row/row"; let num = 0; const data = Array(30000) .fill() .map((val, index) => { if (index % 500 === 0) { num = 0; } return { id: num++, key: `square-${index}` }; }); const chunkArray = (array, chunkSize) => { const results = []; let index = 1; while (array.length) { results.push({ items: array.splice(0, chunkSize), key: String(index) }); index++; } return results; }; const chunks = chunkArray(data, 500); const App = () => { const [count, setCount] = useState(0); const [allItems, setAllItems] = useState(chunks); const onClick = useCallback( (id, index) => { const chunk = [...allItems[index].items]; chunk[id].clicked = true; setCount(val => val + 1); allItems[index].items = chunk; setAllItems(allItems); }, [allItems] ); return (); }; export default App;Count: {count}
{allItems.map(({ items, key }, index) => ())}
// Row component import React, { useCallback } from "react"; import Square from "../square/square"; const Row = ({ items, onClick, index }) => { const onItemClick = useCallback( id => { onClick(id, index); }, [onClick, index] ); return ( <> {items.map(({ id, key, clicked }) => ())} ); }; export default React.memo(Row);
Давайте снова профиль. Мы не видим никакой отставания сейчас. У нас есть намного меньше Ряд
компоненты, поэтому сравнение опора довольно быстро, а также реагирует может пропустить Квадрат
Сравнение опор, если Ряд
Реквисы не изменились.
Вот окончательное приложение Packblitz Предварительный просмотр Код Stackblitz
Отреагировать. Мэмо
и UseCallback
Может быть использован, чтобы получить лучшую производительность. Значит ли это, что мы должны обернуть все компоненты с Реагировать. Мэмо
И все функции с UseCallback
? Нет Отказ Отреагировать. Мэмо
и UseCallback
Используйте памятую, которая добавляет к памяти, также сами функции требуют времени для запуска и иметь накладные расходы, такие как сравнение опор. Расщепление, которое мы сделали, добавляет к памяти также.
Когда использовать REVING.MEMO и USECALLBACK?
Они не требуются, если вы не увидите некоторое отставание в конкретном компоненте или полном приложении. Если есть отставание, попробуйте профилирование для действий на этом экране и проверьте, могут ли быть любые компонент повторные рендеры, которые можно избежать. UseCallback
Также полезен в тех случаях, когда мы используем функции в качестве зависимостей для крючков, чтобы избежать ненужных блоков кода для запуска.
Вывод
В то время как Отреагировать. Мэмо
С UseCallback
, Угемер
Может использоваться для оптимизации производительности приложений реагирования, которые они не требуются в большинстве случаев. Используйте их осторожно.
Оригинал: “https://dev.to/harshdand/react-performance-optimization-tips-4238”