Skip to main content

memo() і стан

Що робить React.memo()

React.memo() - це функція вищого порядку (HOC), яка "обгортає" функціональний компонент і каже React:

"Якщо пропси не змінилися - не перемальовуй цей компонент заново."


Синтаксис

javascript
const MyComponent = React.memo(function MyComponent(props) { // ... });

або у стрілковій формі:

javascript
const MyComponent = React.memo((props) => { // ... });

Як працює React.memo()

Зазвичай React перемальовує компонент при кожному рендері батька, навіть якщо його props не змінилися.

React.memo() робить наступне:

  1. Зберігає попередні пропси компонента.
  2. При наступному рендері порівнює нові та старі пропси (поверхнево).
  3. Якщо пропси не змінилися за посиланням -> компонент не ререндериться.
  4. Якщо хоча б один проп змінився -> компонент перемальовується.

Без React.memo()

javascript
function Child({ value }) { console.log("Child ререндер"); return <p>{value}</p>; } function Parent() { const [count, setCount] = useState(0); return ( <> <button onClick={() => setCount(c => c + 1)}>+</button> <Child value="static text" /> </> ); }

Щоразу при кліку Parent ререндериться, і Child теж ререндериться, хоча його props не змінюються.


З React.memo()

javascript
const Child = React.memo(function Child({ value }) { console.log("Child ререндер"); return <p>{value}</p>; });

Тепер Child не ререндериться, поки його пропси (value) не зміняться.

Продуктивність зростає, особливо при списках, таблицях і великих деревах компонентів.


Як це пов'язано зі станом

  1. Коли ти викликаєш setState у батьківському компоненті -> React ререндерить батька.
  2. За замовчуванням усі дочірні компоненти ререндеряться теж.
  3. React.memo() запобігає цим "зайвим" ререндерам, якщо дочірні компоненти не залежать від зміненого стану батька.

Приклад

javascript
function Parent() { const [count, setCount] = useState(0); const [text, setText] = useState(""); return ( <div> <input value={text} onChange={e => setText(e.target.value)} /> <Counter count={count} /> <button onClick={() => setCount(c => c + 1)}>+</button> </div> ); } const Counter = React.memo(({ count }) => { console.log("Counter ререндер"); return <p>Лічильник: {count}</p>; });

Під час введення тексту Counter не ререндериться, тому що count (його проп) не змінився. Без React.memo() ререндер відбувався б на кожен ввід.


Важливо: поверхневе порівняння (shallow compare)

React.memo() порівнює пропси за посиланням, не глибоко.

Приклад:

javascript
const user = { name: "Tim" }; <Child data={user} />

Якщо при наступному рендері створюється новий об'єкт:

javascript
<Child data={{ name: "Tim" }} />

React.memo все одно перемалює компонент, тому що {} !== {} (різні посилання в пам'яті).

Щоб цього уникнути, мемоізуй об'єкти та функції через:

javascript
useMemo() / useCallback()

Комбінація React.memo + useCallback / useMemo

Дуже часто React.memo використовується разом з мемоізацією колбеків:

javascript
const handleClick = useCallback(() => { console.log("clicked"); }, []); // посилання не змінюється між рендерами <Child onClick={handleClick} />

Без useCallback React буде створювати нову функцію на кожен рендер, і React.memo не допоможе, тому що пропси будуть "новими" щоразу.


Підсумок

Що робить React.memo()Обгортає компонент, щоб він ререндерився тільки при зміні пропсів
Як працюєПоверхнево порівнює старі та нові пропси
Зв'язок зі станомЗапобігає ререндеру дочірніх компонентів, якщо стан батька не впливає на їхні пропси
Ключові інструменти поручuseCallback, useMemo - щоб зберігати стабільні посилання
Коли застосовуватиПри великих деревах, таблицях, списках, важких дочірніх компонентах
Коли не потрібенУ простих компонентах - React сам достатньо оптимізований

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.