React Context API
Context API — это современный способ управления состоянием в React. Предназначен для совместного использования части данных между многими компонентами React без детализации. В этой статье мы разберем недостатки контекстного API, фабрику логических оберток и действий.
В этой статье мы рассмотрим Context API, начиная с понимания его необходимости в приложениях React, до его настройки и эффективного использования. Мы также рассмотрим распространенные варианты использования, обсудим лучшие практики, чтобы вы могли использовать Context API в полной мере.
Понимание необходимости контекста в React
Давайте рассмотрим простой пример, в котором у нас есть , ParentComponent
содержащий некоторое count
состояние, и вам нужно передать это состояние в глубоко вложенный GrandchildComponent
.
Вот , ParentComponent
который хранит состояние, setState
но не использует их:
'use client';
import { useState } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const [count, setCount] = useState(0);
return (
<>
<div className="text-center mt-3">
<h2 className="text-3xl">Parent Component</h2>
<small>Not using the count state</small>
</div>
<ChildComponent count={count} setCount={setCount} />
</>
);
};
export default ParentComponent;
ChildComponent
не использует state и setState
тоже, но все равно должно брать их у ParentComponent
и передавать GrandChildComponent
кто в них нуждается:
'use client';
import GrandChildComponent from './GrandChildComponent';
const ChildComponent = ({ count, setCount }) => {
return (
<>
<div className="text-center mt-3">
<h2 className="text-3xl">Child Component</h2>
<small>Not Using the count state too</small>
</div>
<GrandChildComponent count={count} setCount={setCount} />
</>
);
};
export default ChildComponent;
GrandChildComponent
нуждается в состоянии и setState
, и использует их:
'use client';
const GrandChildComponent = ({ count, setCount }) => {
return (
<div>
<div className="text-center mt-3">
<h2 className="text-3xl">Grandchild Component</h2>
<small>Using the count state</small>
</div>
<div className="text-center">
<h3 className="text-2xl">Count is: {count}</h3>
<button
onClick={() => setCount(count + 1)}
className="bg-pink-600 p-2 rounded text-white"
>
Increase Count
</button>
</div>
</div>
);
};
export default GrandChildComponent;
А вот как все выглядит в браузере после ParentComponent
импорта в Home
компонент проекта Next JS:
Это prop drilling в действии. Вы можете видеть , ChildComponent
которое не использует count
состояние и setCount
все еще должно поглощать оба, потому что они будут использоваться в GrandChildComponent
.
Вот как вы продолжите передавать состояние в приложении. Ну и что, если у вас есть компоненты, которые все еще глубже в дереве? Как GreatGrandChild
и даже GreatGreatGrandChild
? Зачем родителям беспокоить свое молодое поколение своими проблемами?
В более крупных приложениях prop drilling может усложнить поддержку и понимание кода. Каждый промежуточный компонент должен знать о подпорках, которые ему нужно передать, даже если он их не использует.
Вот почему существует Context API, позволяющий предотвратить это громоздкое изучение свойств и сделать использование состояния в глубоко вложенных компонентах менее громоздким и более простым.
Как работает API контекста?
Context API предоставляет средства для совместного использования значений, таких как состояние, функции или любые данные, по всему дереву компонентов без передачи props вручную на каждом уровне. Это особенно полезно для глобальных данных, к которым многим компонентам нужен доступ.
Чтобы начать использовать Context API, первое, что вам нужно сделать, это создать контекст с помощью createContext()
метода. Эта функция возвращает объект контекста с двумя компонентами – a Provider
и a Consumer
.
Используется Provider
для обертывания части дерева компонентов, где вы хотите, чтобы контекст был доступен. Он принимает обязательный value
prop, который содержит данные, которые вы хотите совместно использовать с другими компонентами. Когда value
prop изменяется Provider
, все потомки, которые потребляют контекст, будут повторно визуализированы.
Позволяет Consumer
любому дочернему компоненту использовать контекст. Он принимает функцию в качестве дочернего элемента, где аргумент функции — это текущее значение контекста. В современном React useContext
хук часто используется вместо Consumer
для лучшей читаемости и простоты.
Как настроить поставщика контекста
Чтобы показать вам, как настроить поставщик контекста, я буду использовать состояние счетчика и setCount
функцию из примера prop drilling.
Помните, что первое, что нужно сделать, это создать контекст с помощью createContext
метода. Я сделаю это в context/counterContext.js
файле. Это соглашение по именованию файла контекста – functionalityContext.js
или .ts
.
Вот полные шаги по настройке поставщика контекста:
- Импорт
createContext
иuseState
из React - Создайте
CounterContext
константу и установите ее значениеcreateContext
- Передайте значения по умолчанию
createContext
- Создайте
CounterProvider
компонент, который будет приниматьchildren
- Определите свой
state
иsetState
- Верните a,
CounterContext.Provider
который приметcount
иsetCount
в качестве значенийvalue
свойства - Передача
children
– представляет все, что должно быть вложено при использовании контекста. - Экспорт
CounterContext
иCounterProvider
import { createContext, useState } from 'react';
const CounterContext = createContext({
count: 0,
setCount: () => {},
});
const CounterProvider = ({ children }) => {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
);
};
export { CounterContext, CounterProvider };
Этот же процесс применим к любому контексту, который вы хотите создать.
Как использовать контекст в компонентах React
Чтобы использовать контекст, первое, что вам нужно сделать, это импортировать его и поместить в приложение.
Для нашего небольшого приложения-счетчика вы можете сделать это внутри layout
файла проекта Next JS 14, импортировав данные CounterProvider
из counterContext
файла и заключив их {children}
в body
тег:
import { CounterProvider } from '@/context/counterContext';
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en">
<body className={inter.className}>
{/* Wrap the CounterProvider around the childre */}
<CounterProvider>{children}</CounterProvider>
</body>
</html>
);
}
Теперь все страницы и компоненты будут иметь доступ к count
состоянию и setCount
функциям.
Теперь внутри GrandChild
компонента, где используются count
состояние и функция, импортируйте из файла и из него , затем удалите реквизиты.setCountuseContext'react'CounterContextcounterContext
Также вытащите count
состояние setCount
из CounterContext
импортированного вами файла следующим образом:
const { count, setCount } = useContext(CounterContext);
Вы можете оставить count
и setCount
как есть, и все будет работать нормально:
'use client';
import { useContext } from 'react';
import { CounterContext } from '@/context/counterContext';
const GrandChildComponent = () => {
const { count, setCount } = useContext(CounterContext);
return (
<div>
<div className="text-center mt-3">
<h2 className="text-3xl">Grandchild Component</h2>
<small>Using the count state</small>
</div>
<div className="text-center">
<h3 className="text-2xl">Count is: {count}</h3>
<button
onClick={() => setCount(count + 1)}
className="bg-pink-600 p-2 rounded text-white"
>
Increase Count
</button>
</div>
</div>
);
};
export default GrandChildComponent;
Все по-прежнему работает отлично:
пропов в React
Распространенные варианты использования Context API
Context API универсален и может использоваться в различных сценариях, где необходимо управление состоянием и обмен данными между несколькими компонентами. Вот некоторые распространенные варианты использования:
- Глобальное управление состоянием в средних и крупных приложениях : Context API может осуществлять глобальное управление состоянием, например, элементами корзины в приложении электронной коммерции или текущей воспроизводимой песней в музыкальном приложении.
- Управление аутентификацией : использование Context API и других подобных решений для управления состоянием аутентификации является обычным вариантом использования. Такие состояния, как текущий пользователь и токены аутентификации, могут быть общими для всего приложения с помощью Context API. Это позволяет любому компоненту получать доступ к состоянию аутентификации пользователя и выполнять такие действия, как вход и выход из системы, или отображать определенные элементы на основе состояния.
- Управление темами : Еще один популярный вариант использования Context API — управление темами (переключение темного и светлого режимов). Вы можете сделать это, сохранив состояние темы в контексте, а затем получить доступ к теме и обновить ее в любом компоненте без необходимости передавать свойства через несколько слоев.
Другие варианты использования — локализация, пользовательские настройки, такие как настройки уведомлений, открытие, закрытие и переключение состояний модального окна, управление запросами API, навигация по цепочке переходов, пошаговый прогресс и любые другие моменты, где задействовано состояние.
Лучшие практики использования Context API в React
Если вы хотите эффективно использовать Context API, есть несколько рекомендаций, которым нужно следовать, чтобы ваше приложение оставалось поддерживаемым, производительным и масштабируемым. Вот несколько рекомендаций и советов по использованию Context API:
Всегда указывайте значения по умолчанию
Предоставление значений по умолчанию может помочь избежать неопределенных ошибок при использовании контекста за пределами его поставщика.
const UserContext = createContext({
user: { name: 'Guest', age: null },
setUser: () => {},
});
Не злоупотребляйте контекстом
Чрезмерное использование контекста может привести к проблемам с производительностью и усложнить понимание управления состоянием. Используйте контекст только для глобального состояния, к которому действительно должен иметь доступ множество компонентов.
Это можно сделать, создав несколько контекстов для разных частей вашего приложения вместо одного всеобъемлющего контекста.
const ThemeContext = React.createContext();
const AuthContext = React.createContext();
Это сокращает количество ненужных повторных рендеров и сохраняет модульность управления состоянием.
Избегайте частых обновлений
Используйте локальное состояние для часто меняющихся данных. Это связано с тем, что частое обновление значений контекста может привести к повторной отрисовке всех потребляющих компонентов, что может негативно повлиять на производительность.
const UserContext = createContext();
const UserProvider = ({ children }) => {
const [user, setUser] = useState({ name: 'John Doe', age: 30 });
const [isOnline, setIsOnline] = useState(true); // local state for a frequently changing data
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
Используйте пользовательские хуки для инкапсуляции логики
Попытайтесь создать пользовательские хуки для инкапсуляции логики потребления контекста. Это улучшает читаемость кода и возможность повторного использования.
const useUser = () => {
const context = useContext(UserContext);
if (!context) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
};
Запоминайте значения контекста
Использование useMemo
хука для запоминания значений контекста может помочь предотвратить ненужные повторные визуализации потребляющих компонентов.
const UserProvider = ({ children }) => {
const [user, setUser] = useState({ name: 'John Doe', age: 30 });
const value = useMemo(() => ({ user, setUser }), [user, setUser]);
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
};
Краткое содержание
В этой статье мы изучили Context API, начав с понимания его потребности и того, как он работает. Используя встречный пример, мы настроили Context Provider и использовали контекст в компоненте, чтобы продемонстрировать его использование.