Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що робить "selector pattern" у контексті?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Selector pattern** - це підхід, за якого компонент підписується **тільки на частину контексту**, а не на весь об'єкт `value`, завдяки чому він ререндериться лише при зміні саме того поля, яке читає. **Ключове:** бібліотеки на кшталт `use-context-selector` порівнюють старе й нове вибране значення через `Object.is` і ререндерять компонент, тільки якщо воно справді змінилося.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Проблема, яку він вирішує Звичайний контекст працює так: ```javascript const AppContext = createContext({ user, theme, cart }); const value = { user, theme, cart }; // об'єкт змінюється за посиланням <AppContext.Provider value={value}> <Child /> </AppContext.Provider> ``` Якщо всередині `Child` ти читаєш **тільки** `theme`: ```javascript const { theme } = useContext(AppContext); ``` то при зміні `user` або `cart` `theme` залишається тим самим, **але компонент все одно перерендериться**, тому що `value` змінився **за посиланням**. Це фундаментальна поведінка React Context. Навіть якщо змінилася одна частина, ререндеряться всі споживачі. --- ## Selector pattern: як він це вирішує **Ідея:** компонент підписується **тільки на частину контексту**, а не на весь об'єкт `value`. Тобто: > "Стеж тільки за `theme`, і не реагуй на зміни `user` або `cart`." --- ## Приклад з бібліотекою `use-context-selector` Ця бібліотека реалізує selector pattern для Context API. ### 1. Створюємо контекст ```javascript import { createContext } from "use-context-selector"; export const AppContext = createContext({ user: { name: "Tim" }, theme: "light", cart: [], }); ``` ### 2. Обгортаємо Provider ```javascript import { AppContext } from "./AppContext"; export function AppProvider({ children }) { const [user, setUser] = useState({ name: "Tim" }); const [theme, setTheme] = useState("light"); const [cart, setCart] = useState([]); const value = { user, theme, cart, setUser, setTheme, setCart }; return <AppContext.Provider value={value}>{children}</AppContext.Provider>; } ``` ### 3. Використовуємо селектори у споживачах ```javascript import { useContextSelector } from "use-context-selector"; import { AppContext } from "./AppContext"; function ThemeSwitcher() { const theme = useContextSelector(AppContext, ctx => ctx.theme); const setTheme = useContextSelector(AppContext, ctx => ctx.setTheme); console.log("ThemeSwitcher render"); return ( <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}> {theme} </button> ); } function UserName() { const user = useContextSelector(AppContext, ctx => ctx.user); console.log("UserName render"); return <p>Hello, {user.name}</p>; } ``` Тепер: - При зміні `theme` -> перерендериться тільки `ThemeSwitcher`; - При зміні `user` -> тільки `UserName`; - Інші споживачі залишаться недоторканими. --- ## Чому це працює - Кожен споживач підписується **на конкретне підзначення** з контексту. - `use-context-selector` під капотом створює "міні-підписку" тільки на вибраний фрагмент `value`. - При оновленні контексту React порівнює **старе і нове вибране значення** через `Object.is` - якщо вони рівні, компонент не ререндериться. --- ## Без сторонніх бібліотек (базова реалізація) Можна реалізувати селекторний підхід вручну - **розділивши контексти**: ```javascript export const UserContext = createContext(); export const ThemeContext = createContext(); export const CartContext = createContext(); ``` і споживати рівно той, що потрібен. Це найпростіший аналог "selector pattern", тільки не на рівні значення, а на рівні "контексту як сутності". --- ## Підсумок | Підхід | Поведінка | |---|---| | `useContext` | Компонент підписаний на **все value**, ререндериться при будь-якій зміні | | `use-context-selector` | Компонент підписаний **на вибране поле**, ререндериться тільки при його зміні | | "Багато контекстів" | У кожного своя зона відповідальності - теж варіант selector pattern | --- ### Selector pattern допомагає: зменшити ререндери при частих оновленнях контексту підвищити продуктивність великих дерев компонентів писати гнучкі, реактивні контексти без важких сторів (Redux, Zustand)Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.