Продуктивність React за межами ручного використання memo: структура та планування
Порада з часів компіляторів: розміщуйте стан у одному місці, виділяйте термінову роботу, відправляйте менше JS, а потім мемоайзуйте виявлені проблемні ділянки.
Протягом багатьох років поради щодо продуктивності React полягали у тому, щоб обгортати все за допомогою API memo:
const value = useMemo(() => expensiveCalculation(data), [data]);
const handleClick = useCallback(() => {
doSomething(id);
}, [id]);export default React.memo(Component);
function ProductList({ products, onSelect }) {
const processed = useMemo(
() => processProducts(products),
[products]
);
const handleSelect = useCallback(
(id) => onSelect(id),
[onSelect]
); return (
<List
products={processed}
onSelect={handleSelect}
/>
);
}
App
│
├── Header
├── Sidebar
├── Search
├── ProductList
└── Footer
function App() {
const [query, setQuery] = useState("");
// large component tree
}
App
│
├── Header
├── Sidebar
├── Search
│ └── query state
├── ProductList
└── Footer
const [query, setQuery] = useState("");
const [isPending, startTransition] = useTransition();
function handleChange(event) {
const value = event.target.value; setQuery(value); startTransition(() => {
setSearchResults(filterProducts(value));
});
}
User input
│
├── urgent ───────► keep UI responsive
│
└── non-urgent ───► transition
const AnalyticsDashboard = lazy(
() => import("./AnalyticsDashboard")
);
Initial JavaScript
│
▼
┌─────────────────────┐
│ Everything │
│ Dashboard │
│ Analytics │
│ Editor │
│ Admin │
└─────────────────────┘
Initial load
│
├── Core UI
│
└── Later
├── Analytics
├── Editor
└── Admin
"This component renders often."
│
▼
Add useMemo
"This component renders often."
│
▼
Why?
│
┌──────┼───────────┐
▼ ▼ ▼
State Expensive Large
flow work subtree
│ │ │
▼ ▼ ▼
Colocate Optimize Restructure
1. Measure
↓
2. Find the expensive work
↓
3. Fix component architecture
↓
4. Reduce unnecessary JavaScript
↓
5. Prioritize updates correctly
↓
6. Let React Compiler handle memoization
↓
7. Manually optimize only when evidence says you should
Лише мемоізація не вирішує проблеми повільних додатків. Навіть ідеально мемоізована структура може працювати повільно через надмірний обсяг даних, термінові оновлення, які виконують неважливі завдання, або велику кількість коду JavaScript у основному потоці.
React Compiler змінює стандарт
Компілятор автоматизує багато процедур мемоізації. Це дозволяє зосередитися на структурі та плануванні, а не на ручному використанні useMemo у кожному місці.
По-перше: розміщуйте дані ближче до місця їх використання
Високий рівень стану в дереві змушує переробляти всі піддерева. Розміщуйте стан разом із листками, які його потребують; піднімайте його лише тоді, коли це необхідно для спільного використання.
По-друге: не змушуйте термінові оновлення виконувати неважливі завдання
Зберігайте чутливість до натискання клавіш та анімацій. Відкладайте неважливі завдання за допомогою переходів чи відкладених значень, щоб натискання клавіш не блокувалися дорогими фільтрами.
По-третє: перегляньте JavaScript, який ви використовуєте
Великі цикли синхронізації, дорогі форматувальники та необмежені списки домінують ще до того, як це зробить механізм узгодження React. Аналізуйте JavaScript, а не лише те, що показують інструменти React DevTools.
То коли варто використовувати useMemo?
Він все ще корисний для забезпечення стабільності посилань між дочірніми елементами, які не передають пропси, а також для дуже витратних чистих обчислень — після їх вимірювання. Не як стандартний інструмент для кожного значення.
Новий підхід до продуктивності в React
Структура → планування → відправка меншої кількості JS → потім мікромемоізація найбільш використовуваних ділянок. Мемоізація за допомогою компілятора є допоміжним засобом, а не заміною правильного проектування.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілю React для відомого навантажувального шляху.
Списки повинні віртуалізуватися перед тим, як мемоізувати кожен рядок. Мемоізація при відображенні 10 тис. рядків все одно призводить до втрат продуктивності.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілю React для відомого навантажувального шляху.
Списки повинні віртуалізуватися перед тим, як мемоізувати кожен рядок. Мемоізація при відображенні 10 тис. рядків все одно призводить до втрат продуктивності.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілю React для відомого навантажувального шляху.
Списки повинні віртуалізуватися перед тим, як мемоізувати кожен рядок. Мемоізація при відображенні 10 тис. рядків все одно призводить до втрат продуктивності.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілювання React для відомого навантажувального маршруту.
Списки повинні віртуалізуватися перед тим, як мемоїзувати кожен рядок. Мемоїзація під час завантаження списку з 10 тис. рядків все одно призводить до проблем.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілювання React для відомого навантажувального маршруту.
Списки повинні віртуалізуватися перед тим, як мемоїзувати кожен рядок. Мемоїзація під час завантаження списку з 10 тис. рядків все одно призводить до проблем.
Додайте бюджет продуктивності до PR-запитів: час взаємодії до наступного оновлення інтерфейсу для основної форми, а також знімок профілювання React для відомого навантажувального маршруту.
Списки повинні віртуалізуватися перед тим, як мемоїзувати кожен рядок. Мемоїзація під час завантаження списку з 10 тис. рядків все одно призводить до проблем.