Для стану, яким користуються компоненти на різних рівнях дерева (кошик, список завдань, налаштування), React пропонує поєднання useReducer + Context без сторонніх бібліотек.
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({ children }) {
const [tasks, dispatch] = useReducer(tasksReducer, []);
return (
<TasksContext value={tasks}>
<TasksDispatchContext value={dispatch}>
{children}
</TasksDispatchContext>
</TasksContext>
);
}
export function useTasks() {
const tasks = useContext(TasksContext);
if (tasks === null) throw new Error('useTasks має бути всередині TasksProvider');
return tasks;
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
У React 19 сам контекст можна рендерити як провайдер (<TasksContext value={...}>); старий запис <TasksContext.Provider> теж працює.
Використання в будь-якому компоненті під провайдером:
function AddTask() {
const dispatch = useTasksDispatch();
return <button onClick={() => dispatch({ type: 'added', text: 'Нове завдання' })}>Додати</button>;
}
function TaskList() {
const tasks = useTasks();
return tasks.map((task) => <Task key={task.id} task={task} />);
}
Чому два контексти, а не один:
dispatchстабільний - не змінюється між рендерами;- компоненти, яким потрібна лише відправка дій (кнопки, форми), підписуються лише на
TasksDispatchContextі не перерендерюються, коли змінюється список завдань.
Переваги підходу:
- логіка змін - в одному редукторі, її легко тестувати;
- компоненти не передають колбеки через десятки рівнів;
- власні хуки (
useTasks) приховують деталі й перевіряють наявність провайдера.
Обмеження, через які переходять на сховища:
- будь-яка зміна значення контексту перерендерює всіх його споживачів - навіть тих, кому потрібна лише частина даних. Немає селекторів «підпишись лише на це поле»;
- провайдери, що вкладаються один в один для кожної частини стану, ускладнюють структуру;
- немає інструментів налагодження «з коробки», проміжних обробників (middleware), збереження стану.
Для частих оновлень і великого спільного стану - Zustand, Redux Toolkit чи подібні з підписками на частину стану.
Докладніше в документації: Масштабування з reducer і context