Всем привет! Меня зовут Андрей, я Frontend разработчик. На данный момент работаю на фрилансе. Имею достаточно хороший опыт работы с React.
Хочу рассказать, что такое React Context и как это можно использовать в проектах.
Попробую всё рассказать как можно проще.
Начнем
React context - некий интерфейс, который позволяет сохранять какие-либо данные и передавать их вниз по дереву без передачи пропсов в дочерние компоненты.
Т.е по сути что-то на подобие стора из всем нам известных Redux, MobX, Effector и т.п, но конечно же есть различия и довольно большой минус, о котором мы поговорим ниже, после просмотра примеров кода.
Немного о том, какие методы и компоненты используются в Context.
createContext - объявление контекста (можете воспринимать это как store из redux для большего начального понимания).
Context.Provider - компонент, оборачивающий наши компоненты, которые должны иметь доступ к данным.
useContext - хук, позволяющий получить данные из context.
В целом всё достаточно просто.
Теперь разберём пример, чтобы закрепить понимание.
Допустим нам нужно реализовать смену темы в приложении. Вот как это можно сделать:
import { FC, createContext, useContext, useState, ReactNode } from 'react';
type Theme = 'light' | 'dark';
interface ThemeContextType {
theme: Theme;
toggleTheme: () => void;
}
export const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const ThemeProvider: FC<{ children: ReactNode }> = ({ children }) => {
const [theme, setTheme] = useState<Theme>('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
Как видно из примера, мы создаем context с помощью createContext. В качестве первого параметра указываем начальное значение - undefined. Конечно же там может быть всё что угодно: массив, объект, строка, число и т.д. Через джинерик указываем тип ThemeContextType | undefined.
Далее создаем провайдер - ThemeProvider ( обычный компонент с children, но обернутый в Context.Provider. В value передаем данные, которые будут доступны всем дочерним компонентам.
Теперь перейдем к тому как использовать объявленный context.
Допустим мы хотим дать доступ к этим данным для всех компонентов. Рационально это сделать в каком нибудь layout компоненте:
export const Layout = () => {
return(
<ThemeProvider>
<Header />
<MainContent />
<Footer />
</ThemeProvider>
)
}
Теперь для Header, MainContent, Footer и всем их дочерним компонентам будут доступны theme и toggleTheme из контекста.
Т.е провайдер мы можем, например, использовать и только для MainContent. И тогда theme, toggleTheme будут доступны только для дочерних компонентов MainContent.
В этом примере показываю как юзать данные из контекста:
import {useContext} from "react";
import {ThemeContext} from "какой-то путь"
export const Header = () => {
const {theme, toggleTheme} = useContext(ThemeContext)
return(
<div>
<p>Сейчас включена тема: {theme === 'light' ? "Светлая" : "Тёмная"}</p>
<button onClick={toggleTheme}>Изменить тему</button>
</div>
)
}
В useContext передаем переменную в которой объявили createContext()
В React 19 теперь можем вместо useContext юзать use(), а use использовать в условиях. Круто, правда? Конечно круто)
И так, конечно всё это очень прикольно и интересно, но Context имеет довольно большой минус:
Все, что использует контекст, перерендеривается каждый раз, когда контекст изменяет состояние.
Только компоненты, которые вызывают useContext, повторно ререндерятся при изменении стейта контекста.
Но это не значит, что использовать context не нужно. Нужно. Просто аккуратно и отслеживая производительность. Часто я использовал context в проектах не замечая каких-либо падений производительности вообще.
Вот как раз этот минус закрывается при использовании Redux и MobX. Но у Вас может возникнуть вопрос: "Как же нам помогают эти либы, если они тоже используют Context из React ?".
Вот так:
Хранилище, которое разделяют эти библиотеки управления состоянием с контекстом, немного отличается от совместного использования состояния непосредственно с контекстом. Когда вы используете Redux и Mobx, работает алгоритм сравнения, который обеспечивает повторный рендеринг только тех компонентов, которые действительно необходимы для визуализации.
Подведу небольшой итог
React Context — это удобный инструмент для управления состоянием, который позволяет передавать данные через дерево компонентов без использования пропсов. Он прост в использовании, но требует внимательного подхода из-за особенностей с перерендером компонентов при изменении состояния.
Кому интересно, веду блог в telegram - https://t.me/budn1_codera
Автор: teryaevandrey