Перейти к содержимому

8. useContext

![Иллюстрация к уроку](/lessons/react-use-context.png)
`useContext` - это хук в React, который позволяет получать доступ к значениям контекста в функциональных компонентах. Он помогает избежать прокидывания пропсов через множество промежуточных компонентов, упрощая структуру приложения.
## Концепция useContext
Представьте, что у вас есть глобальная переменная, к которой должны иметь доступ многие компоненты в вашем приложении. Вместо того, чтобы передавать эту переменную как пропс каждому компоненту по цепочке, вы можете использовать `useContext`. Context позволяет предоставить некоторое значение всем компонентам, находящимся в его дереве.
### Создание контекста
Сначала нужно создать контекст с помощью `React.createContext()`.
```javascript
import React from 'react';
const ThemeContext = React.createContext('light'); // 'light' - значение по умолчанию

Затем нужно предоставить значение контекста с помощью ThemeContext.Provider. Все компоненты, находящиеся внутри ThemeContext.Provider, смогут получить доступ к значению контекста.

import React from 'react';
import ThemeContext from './ThemeContext'; // Предполагаем, что ThemeContext находится в отдельном файле
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}

Внутри дочерних компонентов можно использовать хук useContext для получения доступа к значению контекста.

import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ backgroundColor: theme === 'dark' ? 'black' : 'white', color: theme === 'dark' ? 'white' : 'black' }}>
Я кнопка с темой: {theme}
</button>
);
}

Пример 1: Передача данных пользователя

import React, { createContext, useContext, useState } from 'react';
const UserContext = createContext(null);
function UserProvider({ children }) {
const [user, setUser] = useState({ name: 'Guest', isLoggedIn: false });
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
function Profile() {
const { user } = useContext(UserContext);
return (
<div>
{user.isLoggedIn ? `Welcome, ${user.name}!` : 'Please log in.'}
</div>
);
}
function LoginButton() {
const { setUser } = useContext(UserContext);
const handleLogin = () => {
setUser({ name: 'John Doe', isLoggedIn: true });
};
return <button onClick={handleLogin}>Login</button>;
}
function App() {
return (
<UserProvider>
<Profile />
<LoginButton />
</UserProvider>
);
}
export default App;

Пример 2: Управление темой приложения

(Этот пример расширяет пример выше, показывая переключение темы)

ThemeContext.js
import React, { createContext } from 'react';
const ThemeContext = createContext({ theme: 'light', toggleTheme: () => {} });
export default ThemeContext;
// App.js
import React, { useState } from 'react';
import ThemeContext from './ThemeContext';
import ThemedComponent from './ThemedComponent';
function App() {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(theme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<ThemedComponent />
</ThemeContext.Provider>
);
}
export default App;
// ThemedComponent.js
import React, { useContext } from 'react';
import ThemeContext from './ThemeContext';
function ThemedComponent() {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ backgroundColor: theme === 'light' ? 'white' : 'black', color: theme === 'light' ? 'black' : 'white' }}>
<h1>Themed Component</h1>
<button onClick={toggleTheme}>Toggle Theme</button>
</div>
);
}
export default ThemedComponent;

useContext широко используется в реальных проектах для управления глобальным состоянием и конфигурацией.

  • Redux/MobX: Эти библиотеки управления состоянием часто используют Context API React под капотом для предоставления доступа к хранилищу данных компонентам.
  • Тематизация (Material UI, Ant Design): Фреймворки UI-компонентов используют Context API для передачи темы приложения (цвета, шрифты и т.д.) всем компонентам, обеспечивая консистентный внешний вид.
  • Аутентификация: Context часто используется для хранения информации о текущем пользователе и его статусе аутентификации, что позволяет легко контролировать доступ к различным частям приложения.
  • Next.js/Gatsby: При работе с этими фреймворками, useContext может быть использован для передачи данных, полученных на сервере, в компоненты на клиенте.
  • useContext упрощает доступ к глобальным данным, избегая прокидывания пропсов.
  • React.createContext() создает новый контекст.
  • Context.Provider предоставляет значение контекста для дочерних компонентов.
  • useContext(Context) позволяет получить доступ к значению контекста внутри функционального компонента.
  • Context полезен для управления темами, аутентификацией и другими глобальными настройками приложения.
## Интерактивный пример
<Playground client:load
template="react"
files={{
"/App.tsx": `import React, { useState, useContext, createContext } from 'react';
// 1. Создаем контекст
const ThemeContext = createContext('light');
// 2. Дочерний компонент, потребляющий контекст
function ThemedBox() {
// Получаем текущее значение контекста напрямую
const theme = useContext(ThemeContext);
const isDark = theme === 'dark';
return (
<div style={{
padding: '20px',
marginTop: '15px',
borderRadius: '8px',
backgroundColor: isDark ? '#282c34' : '#eee',
color: isDark ? 'white' : '#333',
transition: 'all 0.2s'
}}>
<p>Значение из контекста: <b>{theme}</b></p>
<p>Пропсы не использовались!</p>
</div>
);
}
// 3. Родительский компонент с провайдером
export default function App() {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={theme}>
<div style={{ padding: '20px', fontFamily: 'sans-serif' }}>
<button
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
style={{ padding: '8px 12px', cursor: 'pointer' }}
>
Сменить тему
</button>
{/* Компонент внутри Provider имеет доступ к контексту */}
<ThemedBox />
</div>
</ThemeContext.Provider>
);
}
` }} />