Skip to main content

Оптимізація контексту

1) Ділиш контекст на смислові частини

Не клади все в один "товстий" value.

  • Було: AppContext = { user, theme, notifications, cart }
  • Стало: AuthContext, ThemeContext, UIContext, CartContext

Так зміни в кошику не тригерять перемальовування підписників теми і т.д.


2) Стабілізуй value у Provider

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

javascript
function AuthProvider({ children }) { const [user, setUser] = useState<User | null>(null); // функції теж стабілізуємо const login = useCallback((u: User) => setUser(u), []); const logout = useCallback(() => setUser(null), []); // value змінюється тільки коли змінюється user const value = useMemo(() => ({ user, login, logout }), [user]); return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; }

Уникай:

javascript
<AuthContext.Provider value={{ user, login: (u)=>setUser(u) }}>

(кожен ререндер створює новий об'єкт/функції -> усі підписники оновляться).


3) Розділяй "стан" і "диспатч" на два контексти

dispatch/екшени стабільно-посилальні, їх можна віддавати окремим провайдером:

javascript
const CounterStateContext = createContext<number>(0); const CounterDispatchContext = createContext<React.Dispatch<Action>>(() => {}); function CounterProvider({ children }) { const [count, dispatch] = useReducer(reducer, 0); return ( <CounterDispatchContext.Provider value={dispatch}> <CounterStateContext.Provider value={count}> {children} </CounterStateContext.Provider> </CounterDispatchContext.Provider> ); } // Підписник, якому потрібна лише відправка дій, не ререндериться при зміні count

4) Став Provider якомога нижче в дереві

Чим менше підписників усередині області дії Provider, тим менший масштаб оновлень.

  • Обгортай потрібні гілки, а не весь <App />.

5) Використовуй селектори (точкові підписки)

Бібліотека use-context-selector дозволяє підписуватися на частину value, а не на весь об'єкт.

javascript
import { createContext, useContextSelector } from 'use-context-selector'; const UserContext = createContext<{user: User, setUser:Fn}>(/* ... */); function UserName() { const name = useContextSelector(UserContext, v => v.user.name); return <span>{name}</span>; // Ререндериться тільки якщо змінилося ім'я }

Це дає гранулярні оновлення як у Redux.


6) Обгортки-селектори + React.memo

Іноді зручно зробити маленький компонент-селектор, який читає контекст і віддає рівно потрібний проп вниз мемоізованому компоненту.

javascript
const PriceView = React.memo(function PriceView({ price }: { price: number }) { return <span>{price}</span>; }); function PriceFromContext() { const price = useContext(CartContext).totalPrice; // тільки одне поле return <PriceView price={price} />; }

React.memo тут "відрізає" зайві ререндери нижче, якщо проп не змінився.


7) Не зберігай у контексті "товсті"/часто змінювані структури

  • Клади ідентифікатори і прості дані.
  • Важкі колекції/кеш тримай у зовнішньому сторі (useSyncExternalStore, Zustand/Jotai/Redux) або локально на місці.

8) Для частих оновлень - краще зовнішній стор

Context не про high-frequency оновлення (таймери, курсор, введення). Для таких задач:

  • Zustand/Jotai/Redux + селектори
  • useSyncExternalStore із власною шиною підписок Вони оновлюють лише тих, хто реально читає частину, що змінилася.

9) Часті дрібниці, які рятують

  • Не передавай у value нові масиви/об'єкти без useMemo.
  • Екшени/колбеки - через useCallback.
  • У Provider не обчислюй важке на льоту - мемоізуй або виноси.
  • У TypeScript можна типізувати контексти окремо: State і Actions - менше спокуси "склеїти все в одне".

Коротка "шпаргалка" вибору

  • Рідкі оновлення + прості дані -> Context (із п.1-6 вище).
  • Багато різних полів, що часто змінюються -> Розділити контексти або перейти на стор із селекторами.
  • Дуже часті оновлення (введення, курсор, розміри вікна) -> зовнішній стор/підписка (useSyncExternalStore, Zustand тощо).

Якщо хочеш, накину міні-рефактор: надішли свій поточний Provider/підписників - перетворю на варіант із розділеними контекстами і селекторами.

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

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

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