Контекст провайдер в React: Что это и как его правильно использовать
Контекст провайдер (Context Provider) в React — это мощный инструмент для передачи данных через дерево компонентов, избегая необходимости явно передавать пропсы на каждом уровне. Это особенно полезно, когда данные, такие как тема, авторизация пользователя или настройки приложения, должны быть доступны во многих компонентах.
Что такое контекст в React?
Контекст позволяет создавать глобальные переменные для React-приложения. Он состоит из двух основных частей:
1. Контекст — создаётся с помощью
2. Провайдер (Provider) — оборачивает компоненты, которым необходимо передать значения.
Пример использования контекста
1. Создание контекста
2. Использование провайдера
Провайдер оборачивает компоненты, которые должны получать данные из контекста.
3. Потребление контекста в дочерних компонентах
В дочерних компонентах доступ к данным осуществляется через
Контекст провайдер в React — это мощный инструмент для управления состоянием на уровне приложения. Правильное использование контекста упрощает структуру кода и делает приложение более поддерживаемым. Однако всегда помните, что контекст — это не замена сложных менеджеров состояния, и его следует использовать с умом.
Контекст провайдер (Context Provider) в React — это мощный инструмент для передачи данных через дерево компонентов, избегая необходимости явно передавать пропсы на каждом уровне. Это особенно полезно, когда данные, такие как тема, авторизация пользователя или настройки приложения, должны быть доступны во многих компонентах.
Что такое контекст в React?
Контекст позволяет создавать глобальные переменные для React-приложения. Он состоит из двух основных частей:
1. Контекст — создаётся с помощью
React.createContext
и служит хранилищем значений.2. Провайдер (Provider) — оборачивает компоненты, которым необходимо передать значения.
Пример использования контекста
1. Создание контекста
import React, { createContext, useState } from 'react';
// Создаём контекст
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
2. Использование провайдера
Провайдер оборачивает компоненты, которые должны получать данные из контекста.
import React from 'react';
import { ThemeProvider } from './ThemeContext';
import App from './App';
const Root = () => (
<ThemeProvider>
<App />
</ThemeProvider>
);
export default Root;
3. Потребление контекста в дочерних компонентах
В дочерних компонентах доступ к данным осуществляется через
useContext
.import React, { useContext } from 'react';
import { ThemeContext } from './ThemeContext';
const ThemeSwitcher = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div>
<p>Текущая тема: {theme}</p>
<button onClick={toggleTheme}>Сменить тему</button>
</div>
);
};
export default ThemeSwitcher;
Контекст провайдер в React — это мощный инструмент для управления состоянием на уровне приложения. Правильное использование контекста упрощает структуру кода и делает приложение более поддерживаемым. Однако всегда помните, что контекст — это не замена сложных менеджеров состояния, и его следует использовать с умом.
Как избежать ада ререндеров с контекстом в React?
Контекст в React передаёт значения всем дочерним компонентам, и любое изменение значения контекста вызывает ререндер всех потребителей (consumer). Это может существенно повлиять на производительность приложения, особенно если дерево компонентов большое.
Вот несколько стратегий, чтобы избежать "ада ререндеров":
1. Разделяйте контексты для разных данных
Если вы используете один контекст для хранения нескольких типов данных (например, тема, авторизация и настройки), то изменение любой из этих данных вызовет ререндер всех потребителей. Лучше создать отдельный контекст для каждой группы данных.
Пример:
Это позволяет минимизировать область ререндеров, так как изменения в одном контексте не затрагивают другой.
2. Передавайте только необходимые данные
Чем больше данных в контексте, тем больше компонентов может быть затронуто ререндером. Старайтесь передавать только те данные, которые необходимы потребителям.
Плохо:
Лучше:
Теперь компоненты, которым нужно только имя пользователя, не будут ререндериться при изменении функции
3. Используйте мемоизацию контекстных значений
Если значение контекста вычисляется динамически, каждый ререндер провайдера будет пересоздавать объект или функцию, даже если данные не изменились. Это приводит к ререндерам потребителей.
Решение — использовать
Пример:
Теперь значение контекста обновляется только при изменении
4. Потребляйте контекст там, где это необходимо
Избегайте использования
Плохо:
Лучше:
5. Используйте селекторы контекста
Селекторы позволяют подписываться только на определённые части данных контекста. Для этого можно использовать кастомные хуки или библиотеки, такие как reselect-context.
Пример кастомного хука:
Теперь можно подписываться только на
6. Используйте компонент React.memo
Если компонент использует контекст, но не должен ререндериться при его обновлении, можно обернуть его в
Пример:
7. Подключение контекста через компонент-посредник
Если компоненту нужно только обновлять данные контекста, используйте специальный компонент, который изолирует изменения.
Контекст в React передаёт значения всем дочерним компонентам, и любое изменение значения контекста вызывает ререндер всех потребителей (consumer). Это может существенно повлиять на производительность приложения, особенно если дерево компонентов большое.
Вот несколько стратегий, чтобы избежать "ада ререндеров":
1. Разделяйте контексты для разных данных
Если вы используете один контекст для хранения нескольких типов данных (например, тема, авторизация и настройки), то изменение любой из этих данных вызовет ререндер всех потребителей. Лучше создать отдельный контекст для каждой группы данных.
Пример:
// Тема
export const ThemeContext = createContext();
// Авторизация
export const AuthContext = createContext();
Это позволяет минимизировать область ререндеров, так как изменения в одном контексте не затрагивают другой.
2. Передавайте только необходимые данные
Чем больше данных в контексте, тем больше компонентов может быть затронуто ререндером. Старайтесь передавать только те данные, которые необходимы потребителям.
Плохо:
const UserContext = createContext({
user: { id: 1, name: 'Михаил', email: 'example@mail.com' },
updateUser: () => {},
});
Лучше:
const UserContext = createContext();
const UpdateUserContext = createContext();
Теперь компоненты, которым нужно только имя пользователя, не будут ререндериться при изменении функции
updateUser
.3. Используйте мемоизацию контекстных значений
Если значение контекста вычисляется динамически, каждый ререндер провайдера будет пересоздавать объект или функцию, даже если данные не изменились. Это приводит к ререндерам потребителей.
Решение — использовать
useMemo
для мемоизации значения.Пример:
import React, { createContext, useState, useMemo } from 'react';
export const ThemeContext = createContext();
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const value = useMemo(() => ({ theme, toggleTheme: () => setTheme((t) => (t === 'light' ? 'dark' : 'light')) }), [theme]);
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
};
Теперь значение контекста обновляется только при изменении
theme
.4. Потребляйте контекст там, где это необходимо
Избегайте использования
useContext
в компонентах, где данные контекста не нужны. Чем меньше компонентов напрямую потребляют контекст, тем меньше ререндеров.Плохо:
const Parent = () => {
const { theme } = useContext(ThemeContext); // Используется только в дочернем компоненте
return <Child />;
};
Лучше:
const Parent = () => <Child />;
const Child = () => {
const { theme } = useContext(ThemeContext);
return <p>Тема: {theme}</p>;
};
5. Используйте селекторы контекста
Селекторы позволяют подписываться только на определённые части данных контекста. Для этого можно использовать кастомные хуки или библиотеки, такие как reselect-context.
Пример кастомного хука:
export const useTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useTheme must be used within a ThemeProvider');
return context.theme;
};
export const useToggleTheme = () => {
const context = useContext(ThemeContext);
if (!context) throw new Error('useToggleTheme must be used within a ThemeProvider');
return context.toggleTheme;
};
Теперь можно подписываться только на
theme
или toggleTheme
, а не на оба одновременно.6. Используйте компонент React.memo
Если компонент использует контекст, но не должен ререндериться при его обновлении, можно обернуть его в
React.memo
.Пример:
const ThemedComponent = React.memo(() => {
const { theme } = useContext(ThemeContext);
return <div>Текущая тема: {theme}</div>;
});
7. Подключение контекста через компонент-посредник
Если компоненту нужно только обновлять данные контекста, используйте специальный компонент, который изолирует изменения.
Пример:
Таким образом, ререндер затронет только
Заключение
Чтобы избежать "ада ререндеров" при использовании контекста в React:
1. Разделяйте данные на несколько контекстов.
2. Используйте
3. Подключайте контекст только в необходимых местах.
4. Используйте кастомные хуки и мемоизацию для работы с контекстом.
Применяя эти подходы, вы сможете эффективно управлять состоянием приложения и обеспечить высокую производительность.
const ThemeUpdater = () => {
const { toggleTheme } = useContext(ThemeContext);
return <button onClick={toggleTheme}>Сменить тему</button>;
};
Таким образом, ререндер затронет только
ThemeUpdater
, а не всю структуру.Заключение
Чтобы избежать "ада ререндеров" при использовании контекста в React:
1. Разделяйте данные на несколько контекстов.
2. Используйте
useMemo
для мемоизации значений.3. Подключайте контекст только в необходимых местах.
4. Используйте кастомные хуки и мемоизацию для работы с контекстом.
Применяя эти подходы, вы сможете эффективно управлять состоянием приложения и обеспечить высокую производительность.
🔍 Интересный JavaScript-кейс:
const obj = {
"1": "a",
1: "6",
[1]: "B"
};
console.log(obj["1"]);
💡 CSS-переменные — что это и зачем они нужны?
CSS-переменные (или custom properties) — это мощный инструмент, который позволяет задавать повторно используемые значения прямо в CSS. Они работают как переменные в JavaScript, но предназначены для стилей. 🎨
📌 Как задать CSS-переменную?
Переменные определяются в специальном синтаксисе
📌 Как использовать?
С помощью функции
📌 Зачем это нужно?
1. 📋 Удобство: Меняете переменную — и обновляете дизайн сразу во всех местах.
2. 🎯 Темизация: Легко переключать темы (светлая/тёмная, брендовые цвета).
3. 🛠 Гибкость: Позволяют динамически изменять стили через JavaScript:
📌 Поддержка
CSS-переменные поддерживаются всеми современными браузерами (кроме IE 😅). Так что смело используйте!
CSS-переменные (или custom properties) — это мощный инструмент, который позволяет задавать повторно используемые значения прямо в CSS. Они работают как переменные в JavaScript, но предназначены для стилей. 🎨
📌 Как задать CSS-переменную?
:root {
--main-color: #3498db;
--padding-size: 16px;
}
Переменные определяются в специальном синтаксисе
--имя-переменной
и обычно задаются в :root
, чтобы они применялись глобально.📌 Как использовать?
button {
background-color: var(--main-color);
padding: var(--padding-size);
}
С помощью функции
var()
можно подключать переменные в любых свойствах.📌 Зачем это нужно?
1. 📋 Удобство: Меняете переменную — и обновляете дизайн сразу во всех местах.
2. 🎯 Темизация: Легко переключать темы (светлая/тёмная, брендовые цвета).
3. 🛠 Гибкость: Позволяют динамически изменять стили через JavaScript:
document.documentElement.style.setProperty('--main-color', '#e74c3c');
📌 Поддержка
CSS-переменные поддерживаются всеми современными браузерами (кроме IE 😅). Так что смело используйте!
🔥 Emits в Vue: Как правильно передавать события 🔥
Если вы работаете с компонентами Vue, вы наверняка сталкивались с ситуацией, когда нужно передать событие из дочернего компонента в родительский. Для этого используются emits.
Что такое emits?
Пример использования:
Дочерний компонент:
Родительский компонент:
Почему это важно?
1️⃣ Ясность и предсказуемость: Выявление всех событий в одном месте (через
2️⃣ Типизация событий: Если вы используете TypeScript, можно типизировать события:
3️⃣ Снижение ошибок: Если вы попробуете отправить событие, не описанное в
📌 Совет: Используйте
Если вы работаете с компонентами Vue, вы наверняка сталкивались с ситуацией, когда нужно передать событие из дочернего компонента в родительский. Для этого используются emits.
Что такое emits?
emits
— это способ декларации событий, которые компонент может отправлять. Это делает ваш код более предсказуемым, типизированным и удобным для отладки.Пример использования:
Дочерний компонент:
<script setup>
import { defineEmits } from 'vue';
// Декларируем событие "submit"
const emit = defineEmits(['submit']);
function handleSubmit() {
emit('submit', { message: 'Данные отправлены!' });
}
</script>
<template>
<button @click="handleSubmit">Отправить</button>
</template>
Родительский компонент:
<script setup>
function onSubmit(payload) {
console.log(payload.message); // Данные отправлены!
}
</script>
<template>
<ChildComponent @submit="onSubmit" />
</template>
Почему это важно?
1️⃣ Ясность и предсказуемость: Выявление всех событий в одном месте (через
defineEmits
) упрощает понимание кода. Вы всегда знаете, какие события ожидаются.2️⃣ Типизация событий: Если вы используете TypeScript, можно типизировать события:
<script setup lang="ts">
const emit = defineEmits<{
(event: 'submit', payload: { message: string }): void;
}>();
</script>
3️⃣ Снижение ошибок: Если вы попробуете отправить событие, не описанное в
defineEmits
, Vue выведет предупреждение в консоль.📌 Совет: Используйте
defineEmits
вместе с defineProps
, чтобы максимально упростить связь между компонентами.🚀 Скоро запуск аналитического крипто-проекта!
Готовлю платформу, которая поможет трейдерам получать актуальную информацию о рынке. В группу буду выкладывать детали разработки — проектирование и разработка сайта, ботов для Telegram, а также клиентской и серверной инфраструктуры. Всё это также можно будет смотреть в реальном времени на стримах в Twitch.
Подписывайтесь, чтобы следить за процессом: 👉 www.twitch.tv/m34r7
Готовлю платформу, которая поможет трейдерам получать актуальную информацию о рынке. В группу буду выкладывать детали разработки — проектирование и разработка сайта, ботов для Telegram, а также клиентской и серверной инфраструктуры. Всё это также можно будет смотреть в реальном времени на стримах в Twitch.
Подписывайтесь, чтобы следить за процессом: 👉 www.twitch.tv/m34r7
🔄 Event Loop в JavaScript — как это работает?
JavaScript — однопоточный язык, но при этом он умеет обрабатывать асинхронные задачи. Как? За счёт Event Loop!
🚀 Как это работает?
1️⃣ Call Stack — выполняет код синхронно. Если функция завершилась, удаляем её из стека.
2️⃣ Web APIs — браузер обрабатывает асинхронные задачи (setTimeout, fetch, DOM события).
3️⃣ Queue — готовые к выполнению колбэки и промисы.
4️⃣ Event Loop — проверяет стек и очереди, двигая выполнение вперёд.
🔥 Что важно знать?
• Microtasks (промисы, MutationObserver) выполняются до рендеринга и перед setTimeout.
• setTimeout(0) не выполняется мгновенно — он ждёт очистки стека.
• async/await — это просто сахар над промисами и всё равно попадает в microtask queue.
JavaScript — однопоточный язык, но при этом он умеет обрабатывать асинхронные задачи. Как? За счёт Event Loop!
🚀 Как это работает?
1️⃣ Call Stack — выполняет код синхронно. Если функция завершилась, удаляем её из стека.
2️⃣ Web APIs — браузер обрабатывает асинхронные задачи (setTimeout, fetch, DOM события).
3️⃣ Queue — готовые к выполнению колбэки и промисы.
4️⃣ Event Loop — проверяет стек и очереди, двигая выполнение вперёд.
🔥 Что важно знать?
• Microtasks (промисы, MutationObserver) выполняются до рендеринга и перед setTimeout.
• setTimeout(0) не выполняется мгновенно — он ждёт очистки стека.
• async/await — это просто сахар над промисами и всё равно попадает в microtask queue.
🔼 Всплытие событий в JavaScript
Когда ты кликаешь по кнопке внутри
🔥 Как это работает?
1️⃣ Событие сначала срабатывает на целевом элементе (куда кликнули).
2️⃣ Затем поднимается вверх по DOM-дереву (родитель, потом его родитель и так до
📌 Пример:
👉 Клик по
Так как
🛑 Как остановить всплытие?
Если не хочешь, чтобы событие всплывало:
⚡️ Полезно знать:
✅
✅
✅ Всплытие можно использовать, например, для делегирования событий (повесить
Когда ты кликаешь по кнопке внутри
div
, почему срабатывает не только click
на кнопке, но и на div
? Это всплытие событий (Event Bubbling)!🔥 Как это работает?
1️⃣ Событие сначала срабатывает на целевом элементе (куда кликнули).
2️⃣ Затем поднимается вверх по DOM-дереву (родитель, потом его родитель и так до
document
).📌 Пример:
document.querySelector("div").addEventListener("click", () => {
console.log("Клик по div!");
});
document.querySelector("button").addEventListener("click", () => {
console.log("Клик по button!");
});
👉 Клик по
button
выведет:Клик по button!
Клик по div!
Так как
click
сначала отработает на кнопке, а затем поднимется к div
.🛑 Как остановить всплытие?
Если не хочешь, чтобы событие всплывало:
event.stopPropagation();
⚡️ Полезно знать:
✅
stopPropagation()
остановит всплытие, но не предотвратит сам обработчик.✅
stopImmediatePropagation()
ещё и заблокирует другие обработчики на этом же элементе.✅ Всплытие можно использовать, например, для делегирования событий (повесить
click
на общий контейнер вместо множества кнопок).🔥 Разница между
Если ты уже немного знаком с JavaScript, то знаешь, что
👉
👉
Но почему
1️⃣
2️⃣ Теперь сравниваем
• JavaScript приводит
•
• Пустая строка
Таким образом,
📌 Вывод:
==
и ===
в JavaScript – почему [] == ![]
возвращает true
?Если ты уже немного знаком с JavaScript, то знаешь, что
==
(нестрогое равенство) и ===
(строгое равенство) работают по-разному.👉
===
сравнивает значение и тип данных:0 === "0"; // false, потому что number !== string
👉
==
выполняет приведение типов (type coercion):0 == "0"; // true, потому что строка "0" приводится к числу 0
Но почему
[] == ![]
→ true? Разбираем пошагово:1️⃣
![]
– пустой массив []
приводится к true
, но оператор !
инвертирует его, превращая в false
.console.log(![]); // false
2️⃣ Теперь сравниваем
[] == false
.• JavaScript приводит
false
к 0
(логическое false
→ числовой 0
).•
[]
в числовом контексте приводится к ""
(пустая строка).• Пустая строка
""
тоже приводится к 0
.Таким образом,
[] == ""
и "" == 0
, а значит:[] == false // true
📌 Вывод:
==
может вести себя неожиданно из-за автоматического приведения типов. Чтобы избежать сюрпризов, используй ===,
если не хочешь неожиданных багов!🚀 Hoisting в JavaScript – почему функции можно вызывать до их объявления?
В JavaScript есть интересная особенность – hoisting (поднятие). Благодаря ему мы можем вызывать функции до их объявления:
Как это работает? Давай разберёмся!
🔍 Что такое hoisting?
Hoisting – это механизм JavaScript, при котором объявления переменных и функций «поднимаются» в начало своего контекста перед выполнением кода.
🔥 Hoisting функций
Функции, объявленные через
JavaScript сначала «видит» всё объявление функции, поэтому вызов работает.
⚠️ Hoisting
Но с переменными всё не так просто:
Переменные, объявленные через
А вот
Переменные, объявленные через
⚠️ Hoisting для
Функции, объявленные через
Здесь
📌 Итог
✅ Function Declaration (обычные функции) поднимаются полностью.
⚠️ Function Expression (
⚠️
❌
💡 Вывод: Hoisting – мощная особенность JavaScript, но будь внимателен с
В JavaScript есть интересная особенность – hoisting (поднятие). Благодаря ему мы можем вызывать функции до их объявления:
hello(); // "Привет!"
function hello() {
console.log("Привет!");
}
Как это работает? Давай разберёмся!
🔍 Что такое hoisting?
Hoisting – это механизм JavaScript, при котором объявления переменных и функций «поднимаются» в начало своего контекста перед выполнением кода.
🔥 Hoisting функций
Функции, объявленные через
function
, полностью поднимаются, поэтому их можно вызывать до определения:sayHi(); // "Привет!"
function sayHi() {
console.log("Привет!");
}
JavaScript сначала «видит» всё объявление функции, поэтому вызов работает.
⚠️ Hoisting
var
и let/const
Но с переменными всё не так просто:
console.log(name); // undefined
var name = "Alice";
Переменные, объявленные через
var
, поднимаются, но без значений (они инициализируются undefined
).А вот
let
и const
ведут себя иначе:console.log(age); // ReferenceError
let age = 25;
Переменные, объявленные через
let
и const
, поднимаются, но остаются в «мертвой зоне» (TDZ) до момента инициализации, поэтому доступ к ним до объявления вызывает ошибку.⚠️ Hoisting для
function expression
Функции, объявленные через
const
или let
, не поднимаются полностью:greet(); // ❌ TypeError: greet is not a function
const greet = function() {
console.log("Привет!");
};
Здесь
greet
ведёт себя как переменная const
, а не как function
, поэтому в момент вызова она ещё не инициализирована.📌 Итог
✅ Function Declaration (обычные функции) поднимаются полностью.
⚠️ Function Expression (
const func = function() {}
) поднимаются как переменные и недоступны до объявления.⚠️
var
поднимается, но остаётся undefined
.❌
let
и const
поднимаются, но остаются в TDZ и вызывают ошибку при доступе до объявления.💡 Вывод: Hoisting – мощная особенность JavaScript, но будь внимателен с
let
, const
и function expression
, чтобы избежать неожиданных ошибок!