Управление состоянием в Next.js 13 с помощью React Context API

Next.js предоставляет несколько подходов к управлению состоянием. Некоторые из них требуют установки внешних библиотек, но React Context API встроен в React, поэтому он помогает снизить зависимость от внешних пакетов.
React Context позволяет удобно передавать данные через дерево компонентов, избавляя от проп-дриллинга. Это особенно полезно для глобального состояния: статус аутентификации пользователя, выбранная тема, языковые настройки и подобное.
Понимание React Context API
Перед тем как перейти к коду, важно понять, что такое React Context API и какую проблему он решает.
Props — простой способ передачи данных от родителя к потомкам. Они хорошо показывают, какие компоненты используют данные и как они текут вниз по дереву.
Однако когда компоненты глубоко вложены и всем нужно одни и те же данные, код может усложниться из‑за постоянной передачи props через промежуточные компоненты. Эту проблему называют prop drilling.

React Context решает эту задачу: вы создаёте централизованное хранилище данных (контекст), а компоненты получают к нему доступ напрямую. Это упрощает архитектуру и делает код более читабельным.
Важно: Context не заменяет всю логику управления состоянием — он подходит для «глобальных» данных. Для высоконагруженных динамических состояний или сложных структур состояния иногда нужны другие решения.
Вы можете найти код этого проекта в его репозитории на GitHub.
Начало работы в Next.js 13 с React Context API
Next.js Server Components дают возможность объединить интерактивность клиентских приложений с производительностью серверного рендеринга. В Next.js 13 Server Components включены по умолчанию в директории app. Поскольку серверные компоненты не могут использовать клиентские API напрямую, при интеграции клиентских библиотек (включая Context в клиентском режиме) возникает необходимость пометить файлы как клиентские.
Для этого в файлах клиентских компонентов нужно указать директиву “use client” вверху файла.
Для начала создайте проект Next.js 13 локально:
npx create-next-app@latest next-context-apiПерейдите в каталог проекта:
cd next-context-apiЗапустите dev‑сервер:
npm run devТеперь можно создать простое приложение To‑Do, которое использует React Context для управления состоянием.
Создание провайдера контекста
Файл провайдера — это центр, где вы определяете глобальное состояние, редьюсер и методы обновления.
Создайте файл src/context/Todo.context.js и добавьте следующий код.
"use client"
import React, { createContext, useReducer } from "react";
const initialState = {
todos: [],
};
const reducer = (state, action) => {
switch (action.type) {
case "ADD_TODO":
return { ...state, todos: [...state.todos, action.payload] };
case "DELETE_TODO":
return { ...state, todos: state.todos.filter((todo, index) =>
index !== action.payload) };
case "EDIT_TODO":
const updatedTodos = state.todos.map((todo, index) =>
index === action.payload.index ? action.payload.newTodo : todo);
return { ...state, todos: updatedTodos };
default:
return state;
}
};
export const TodoContext = createContext({
state: initialState,
dispatch: () => null,
});
export const TodoContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
{children}
);
};Этот код создаёт TodoContext с пустым массивом задач. Редьюсер обрабатывает три типа действий: ADD_TODO, DELETE_TODO и EDIT_TODO. Компонент TodoContextProvider оборачивает приложение и передаёт { state, dispatch } всем вложенным компонентам.
Ключевые понятия:
- Context — контейнер для общих данных.
- Provider — компонент, который делает эти данные доступными.
- Reducer — чистая функция, описывающая, как состояние меняется по действию.
Добавление провайдера в приложение Next.js
Чтобы провайдер был доступен всем компонентам, подключите его в корневом layout. Откройте src/app/layout.js и оберните children в TodoContextProvider:
import './globals.css';
import { TodoContextProvider } from "@/context/Todo.context";
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children
}) {
return (
{children}
);
}После этого все клиентские компоненты, находящиеся внутри app, смогут использовать контекст.
Создание компонента To‑Do
Создайте src/components/Todo.js и пометьте файл как клиентский с “use client”.
Импорт и начало файла:
"use client"
import { TodoContext } from "@/context/Todo.context";
import React, { useContext, useState } from "react";
Основной компонент (JSX):
export default function Todo() {
return (
Todos
setTodoText(e.target.value)}
style={{ marginBottom: 16}}
placeholder="Enter a todo"
/>
{state.todos.map((todo, index) => (
-
{index === editingIndex ? (
<>
setEditedTodo(e.target.value)}
/>
>
) : (
<>
{todo}
>
)}
))}
);
} Переменные состояния и обработчики:
const { state, dispatch } = useContext(TodoContext);
const [todoText, setTodoText] = useState("");
const [editingIndex, setEditingIndex] = useState(-1);
const [editedTodo, setEditedTodo] = useState("");
const handleAddTodo = () => {
if (todoText.trim() !== "") {
dispatch({ type: "ADD_TODO", payload: todoText });
setTodoText("");
}
};
const handleDeleteTodo = (index) => {
dispatch({ type: "DELETE_TODO", payload: index });
};
const handleEditTodo = (index, newTodo) => {
dispatch({ type: "EDIT_TODO", payload: { index, newTodo } });
setEditingIndex(-1);
setEditedTodo("");
};Эти обработчики отправляют соответствующие действия в редьюсер контекста и обновляют локальное состояние формы.
Рендер компонента на странице
Импортируйте компонент на страницу. В файле src/app/page.js замените шаблонный код на:
import styles from './page.module.css'
import Todo from '../components/Todo'
export default function Home() {
return (
)
} После этого приложение отображает To‑Do и позволяет управлять задачами через Context.
Когда Context не подходит
Important: React Context удобен, но имеет ограничения. Рассмотрите альтернативы если:
- Большие объёмы частых обновлений: Context вызывает ререндеры всех потребителей при изменении значения. Это может привести к потерям производительности.
- Сложные асинхронные потоки и сайд-эффекты: для них лучше подходят библиотеки с встроенной поддержкой шины событий и middleware (например, Redux Toolkit с Thunks/Sagas).
- Нужна оптимизация селекторов и мемоизация для отдельных частей состояния.
Counterexamples/когда отказ от Context оправдан:
- Приложение с тысячами элементов, где изменения касаются только узкой части состояния.
- Необходимость сложного trace и time-travel отладки.
Альтернативные подходы
- Redux Toolkit — для сложных, предсказуемых состояний с большим количеством логики.
- Zustand — лёгкая и быстрая альтернатива с минимальным API.
- Recoil/Jotai — атомарные состояния для модульности.
- SWR/React Query — для данных, получаемых с сервера (кеширование, синхронизация, фоновые обновления).
Выбор зависит от размера приложения, требований к производительности и привычек команды.
Ментальные модели и эвристика выбора
- Если данные нужны «всюду» (тема, авторизация, настройки) — Context подходит.
- Если изменение данных редкое — Context удобен.
- Если изменения частые и касаются узкой части UI — выбирайте локальное состояние или специализированные менеджеры.
Факто-бокс: ключевые числа и советы
- Когда использовать Context: для 5–10 глобальных сущностей (тема, язык, пользователь, корзина, настройки). (Качественный ориентир.)
- Производительность: при больших состояниях группируйте данные по контекстам (несколько провайдеров вместо одного монолитного).
- Архитектура: держите редьюсер чистым и предсказуемым — это облегчит тестирование.
Чек-лист для разработчика
- Пометить клиентские файлы директивой “use client”.
- Создать отдельный каталог для контекстов (src/context).
- Разбивать глобальное состояние на несколько контекстов по зонам ответственности.
- Покрыть редьюсер unit-тестами.
- Не хранить в контексте нестабильные объекты (функции без мемоизации).
Критерии приёмки
- Приложение рендерит компонент To‑Do и корректно добавляет, редактирует и удаляет задачи.
- Контекст доступен из клиентских компонентов, обёрнутых провайдером в layout.
- Отсутствует ненужный prop drilling для глобальных данных.
- Производительность приемлема: изменение одного todo не приводит к заметным замедлениям.
План миграции к Context (микроплан)
- Выделить данные, которые действительно нужны глобально.
- Создать контекст и провайдер с минимальным initialState.
- Обернуть app в провайдер и постепенно переводить компоненты с props на context.
- Покрыть поведение редьюсера тестами.
- Оптимизировать: разделить контексты, мемоизировать селекторы.
Тест-кейсы и приёмка
- Добавление: при вводе текста и клике “Add” задача появляется в списке и в state.todos.
- Удаление: при клике “Delete” задача исчезает и индекс корректно применяется.
- Редактирование: в режиме редактирования поле сохраняет новое значение по клику “Save”.
- Граничные условия: пустая строка не добавляется; индексы вне диапазона игнорируются.
Безопасность и приватность
- Context не должен хранить секреты (пароли, токены) в виде plain text, если есть риск утечки в клиентском коде.
- Для авторизации храните токены в безопасном месте (HTTP-only cookies) и передавайте в контексте только метаданные пользователя (имя, роли).
- Для GDPR: если context содержит персональные данные, обеспечьте явное управление сроками хранения и удалением.
Совместимость и советы по миграции Next.js 13
- В app‑директории серверные компоненты по умолчанию. Клиентские компоненты обязаны начинаться с “use client”.
- Провайдеры контекста, использующие клиентский API, должны быть клиентскими компонентами и размещены внутри корневого layout, если их должны видеть все страницы.
- Разделяйте провайдеры: вместо одного глобального создавайте провайдеры для независимых областей (AuthProvider, ThemeProvider, TodoProvider).
Рекомендации по отладке
- Используйте React DevTools — вкладка Context показывает провайдеры и потребителей.
- Логируйте actions в редьюсере для отслеживания последовательности изменений.
- Добавьте unit-тесты для reducer: проверяйте работу ADD_TODO, DELETE_TODO, EDIT_TODO.
Краткое объявление для команды (100–200 слов)
Мы внедрили React Context в Next.js 13 для управления глобальным состоянием To‑Do приложения. Контекст упрощает доступ к данным без проп-дриллинга и позволяет централизованно управлять списком задач с помощью редьюсера (ADD_TODO, DELETE_TODO, EDIT_TODO). Провайдер зарегистрирован в корневом layout, а клиентские компоненты помечены директивой “use client”. При внедрении мы учли производительность: при необходимости состояние можно разделять на несколько провайдеров. Для сложной логики можно комбинировать Context с Redux или Zustand. Просьба протестировать сценарии добавления, редактирования и удаления задач, а также оценить влияние на ререндеры в ваших ключевых страницах.
Итог
React Context — удобный инструмент для простого и понятного глобального состояния. В Next.js 13 важно корректно помечать клиентские компоненты и располагать провайдер в корневом layout. Для больших и динамичных приложений рассматривайте гибридные архитектуры с более специализированными менеджерами состояния.
Summary:
- Context прост для внедрения и подходит для глобальных настроек.
- Разбивайте состояние на несколько контекстов для производительности.
- Тестируйте редьюсер и отслеживайте ререндеры.
Похожие материалы
ChatGPT для рабочих процессов и продуктивности
Карта Payoneer MasterCard: заказ и использование
SSH на Linux: настройка и безопасный доступ
Как конвертировать PDF в PowerPoint
DISM в Windows 11: восстановление образа и файлов