M3 | WebDev
103 subscribers
105 photos
4 videos
14 links
Your guide to the world of programming 🌐🚀
Download Telegram
💡 Контекст в React: что это такое и зачем он нужен?

Контекст в React – это мощный инструмент для управления состоянием приложения, особенно полезный, когда нужно передавать данные на несколько уровней вложенности компонентов, минуя передачу через пропсы. Давайте разберемся, что он из себя представляет и как с ним работать. 🚀

Что такое Контекст?
Контекст позволяет «делиться» данными между компонентами, не передавая их явно через каждый уровень дерева. Это может быть полезно, когда у вас есть данные, которые должны быть доступны на разных уровнях, например, тема, язык интерфейса, авторизация и т.д.

Примеры использования
Контекст особенно полезен для таких глобальных данных, как:

• Тема (светлая/темная)
• Язык интерфейса (локализация)
• Авторизация пользователя
• Настройки приложения

Как создать Контекст?
Для создания контекста используется React.createContext(). Вот пример простого контекста для темы:


import React, { createContext, useContext } from 'react';

// Создаем контекст
const ThemeContext = createContext('light');

// Компонент-провайдер
function ThemeProvider({ children }) {
const theme = 'dark'; // или 'light'

return (
<ThemeContext.Provider value={theme}>
{children}
</ThemeContext.Provider>
);
}

// Используем контекст в компоненте
function DisplayTheme() {
const theme = useContext(ThemeContext);
return <p>Current theme: {theme}</p>;
}


Когда использовать Контекст?
Контекст нужен, когда:

1. Данные глобальны – например, для всех или нескольких компонентов на разных уровнях вложенности.
2. Пропсы становятся громоздкими – передача данных через пропсы на много уровней может сделать код сложным и неудобным для сопровождения.

На что обратить внимание?
Хотя контекст – мощный инструмент, его следует использовать с осторожностью. Частое обновление данных в контексте может привести к перерендерам всех компонентов, что может негативно сказаться на производительности.

Контекст – это не замена глобального состояния для сложных данных (в таких случаях лучше использовать Redux или RTK Query). Но он идеален для данных, которые редко меняются и необходимы на многих уровнях. 📈
📌 Замыкания в JavaScript

Давайте разберёмся, что такое замыкания и почему они считаются одной из самых важных концепций в JavaScript!

Что такое замыкание? Замыкание — это функция, которая "помнит" своё окружение, то есть доступ к переменным из внешней области, даже после завершения этой внешней функции.

Как это работает?
Когда функция создается внутри другой функции, она получает доступ к переменным этой внешней функции, сохраняя их в своём "облаке". Эти переменные остаются доступными для внутренней функции, даже если внешняя функция уже завершила своё выполнение.

Простой пример:


function createCounter() {
let count = 0;
return function() {
count++;
return count;
};
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3



Здесь counter — это замыкание, которое "помнит" значение переменной count, даже после того, как createCounter завершила выполнение.

🔍 Когда использовать замыкания?

• Для создания приватных переменных и функций
• В коллбэках, когда нужно "запомнить" параметры
• В асинхронном коде, где важно сохранить состояние на момент вызова

Замыкания делают код гибким и мощным, но с ними стоит быть осторожнее: из-за доступа к переменным из внешней области они могут занимать память дольше, чем ожидается.
Уже неделю работаю над новым проектом на Vue, хотите посты про Vue и чем он отличается от React?
Anonymous Poll
90%
Да
10%
Нет
📊 React vs Vue: Технические отличия и статистика использования

🌐 React и Vue — два мощных инструмента для создания пользовательских интерфейсов. Чем они отличаются и какой выбрать? Разберемся!

🚀 React
Технические особенности:
• Использует JSX, сочетающий JavaScript и разметку в одном файле.
• Управление состоянием через библиотеки: Redux, Zustand, или Context API.
• Компоненты основаны на функциях и хуках (например, useState, useEffect).
• Быстрый ререндеринг через Virtual DOM.
• Гибкость: React — это библиотека, а не полный фреймворк. Всё настраивается под нужды проекта.

🌱 Vue
Технические особенности:
• Интуитивно понятный синтаксис, разделение разметки, логики и стилей в отдельных блоках (.vue файлы).
• Встроенные инструменты: Vue Router, Vuex для управления состоянием.
• Поддержка Composition API и Options API для разработки.
• Быстрая интеграция в существующие проекты.
• Эффективное управление DOM через Virtual DOM.

📊 Статистика использования (2024)

React:
• Лидирует среди front-end технологий: используется 44.2% разработчиков (Stack Overflow Developer Survey).
• Популярен в крупных и масштабируемых проектах, таких как Netflix, Instagram.


Vue:
• Используется 18.3% разработчиков.
• Простота и встроенные решения делают его отличным выбором для стартапов и проектов с ограниченными сроками.
• Используется в таких компаниях, как Alibaba, Xiaomi.
В прошлом посте я упомянул Composition API и Options API, сейчас хочу разобрать что это такое.

Composition API и Options API — это два подхода для написания компонентов в Vue.js. Вот их основные отличия:

1. Composition API
Добавлен в Vue 3 для улучшения масштабируемости и организации кода.

Особенности:

Синтаксис: Логика компонента организуется в функции внутри setup().
Гибкость: Позволяет группировать связанные части логики вместе, даже если они касаются разных аспектов компонента.
Масштабируемость: Удобен для крупных проектов с большим количеством функций и переиспользуемым кодом.
Работа с функциями: Использует JavaScript-функции вместо объектной структуры.
Реактивность через ref и reactive: Прямая работа с реактивными данными.
Переиспользование логики: Легче создавать и использовать "композиции" (например, кастомные хуки).

Пример:


<script>
import { ref, computed } from 'vue';

export default {
setup() {
const count = ref(0);
const double = computed(() => count.value * 2);

function increment() {
count.value++;
}

return { count, double, increment };
},
};
</script>


2. Options API
Классический подход, используется с Vue 2 и поддерживается в Vue 3.

Особенности:

Синтаксис: Логика компонента организуется в объекте с ключами (data, methods, computed).
Четкая структура: Легко понять, где искать определенную часть логики (например, методы, вычисляемые свойства).
Простота: Удобен для новичков благодаря декларативному подходу.
Менее гибкий: Для сложных компонентов логика может "расползаться" по разным частям объекта.

Пример:


<script>
export default {
data() {
return {
count: 0,
};
},
computed: {
double() {
return this.count * 2;
},
},
methods: {
increment() {
this.count++;
},
},
};
</script>
🪝 Основные хуки в Vue и их аналоги в React

Vue 3 с введением Composition API привнес подход, похожий на React-хуки. Давайте разберем основные хуки в Vue и сравним их с React!

1. onMounted vs useEffect
• Vue: onMounted — выполняет код, когда компонент смонтирован.
• React: useEffect с пустым массивом зависимостей [ ].

// Vue 3
import { onMounted } from 'vue';

export default {
setup() {
onMounted(() => {
console.log('Component mounted');
});
},
};


// React
import { useEffect } from 'react';

function Component() {
useEffect(() => {
console.log('Component mounted');
}, []);

return <div>Component</div>;
}


2. watch vs useEffect
• Vue: watch наблюдает за реактивными значениями и выполняет действия при их изменении.
• React: useEffect с зависимостями.

// Vue 3
import { ref, watch } from 'vue';

export default {
setup() {
const count = ref(0);

watch(count, (newValue, oldValue) => {
console.log(`Count changed: ${oldValue} -> ${newValue}`);
});

return { count };
},
};


// React
import { useState, useEffect } from 'react';

function Component() {
const [count, setCount] = useState(0);

useEffect(() => {
console.log(`Count changed: ${count}`);
}, [count]);

return <button onClick={() => setCount(count + 1)}>Increment</button>;
}


4. computed vs useMemo
• Vue: computed создает вычисляемое свойство.
• React: useMemo мемоизирует вычисления.

// Vue 3
import { ref, computed } from 'vue';

export default {
setup() {
const count = ref(2);
const squared = computed(() => count.value * count.value);

return { count, squared };
},
};


// React
import { useState, useMemo } from 'react';

function Component() {
const [count, setCount] = useState(2);
const squared = useMemo(() => count * count, [count]);

return (
<div>
<p>Squared: {squared}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}


5. ref vs useRef
• Vue: ref делает значение реактивным.
• React: useRef сохраняет ссылку, которая не вызывает ререндеринг.

// Vue 3
import { ref } from 'vue';

export default {
setup() {
const inputRef = ref(null);

const focusInput = () => {
inputRef.value.focus();
};

return { inputRef, focusInput };
},
};


// React
import { useRef } from 'react';

function Component() {
const inputRef = useRef(null);

const focusInput = () => {
inputRef.current.focus();
};

return <input ref={inputRef} onFocus={focusInput} />;
}
Основные отличия React и Vue лежат в архитектуре, философии и подходах к работе с компонентами и данными. Рассмотрим ключевые различия на уровне ядра:

1. Библиотека (React) vs Фреймворк (Vue)
• React — это библиотека, сосредоточенная на создании пользовательского интерфейса. Она предоставляет только базовые инструменты, такие как компоненты и состояние. Для управления маршрутизацией, состоянием или анимацией нужно подключать сторонние библиотеки.

• Vue — это полноценный фреймворк. Из коробки доступны инструменты для работы с маршрутизацией (vue-router), состоянием (vuex), анимацией и прочими задачами.

2. Подход к описанию компонентов
• React: Компоненты описываются с использованием JSX (JavaScript + разметка). JSX требует базовых знаний JavaScript, так как разметка тесно переплетена с логикой.

function Component() {
return <div>Hello, React!</div>;
}


• Vue: Компоненты описываются декларативно, разделяя разметку, логику и стили в .vue файлах. Также поддерживаются JSX и шаблоны для гибкости.

<template>
<div>Hello, Vue!</div>
</template>

<script>
export default {
data() {
return { message: 'Hello, Vue!' };
},
};
</script>


3. Реактивность
React:
• Использует однонаправленный поток данных (props → state).
• Реактивность основана на перерендеринге компонентов через вызов setState или хуков (useState, useReducer).
• Нет наблюдения за изменениями напрямую — любые изменения требуют вызова setState.

Vue:
• Реактивность встроена в ядро. Данные (data или ref) автоматически отслеживаются через реактивные прокси.
• Изменения автоматически отражаются в DOM, благодаря виртуальному DOM и механизму наблюдения.

4. Virtual DOM
React: Все обновления проходят через Virtual DOM. React пересчитывает дерево компонентов и применяет минимальные изменения к реальному DOM. Это требует большего количества вычислений.

Vue: Также использует Virtual DOM, но благодаря реактивной системе Vue знает, какие конкретно компоненты нужно обновить, что уменьшает накладные расходы.

5. Управление состоянием
React: Нет встроенного механизма управления состоянием. Для сложных случаев нужно использовать внешние библиотеки, такие как Redux, MobX или Zustand.

Vue: Управление состоянием через Vuex или Pinia (в Vue 3). Они предоставляют удобный API для работы с глобальным состоянием.

6. Инструменты разработки
React:
• Инструменты разработчика React DevTools помогают анализировать дерево компонентов и их состояние.
• Экосистема сильно зависит от внешних инструментов, например, Next.js для серверного рендеринга.

Vue:
• Инструменты Vue DevTools показывают дерево компонентов, их реактивные данные и события.
• Фреймворк Vue CLI предлагает мощный набор инструментов для настройки проекта.

7. Философия
React: Гибкость. React предоставляет минимальный набор функций, оставляя разработчику выбор библиотек и подходов. Это дает свободу, но требует больше настроек.

Vue: Простота и интуитивность. Vue предлагает множество решений "из коробки", что упрощает старт работы.

8. Производительность
React: Обновления DOM чуть медленнее в сравнении с Vue, так как React всегда пересчитывает все дерево компонентов. Однако оптимизация возможна через React.memo, useMemo и другие методы.

Vue: Благодаря реактивности Vue обновляет только те части DOM, которые изменились, что делает его более производительным "из коробки".

Подводя итог, React отлично подходит для сложных проектов, где требуется высокая степень кастомизации. Vue, в свою очередь, идеально справляется с задачами быстрого старта и хорошо подходит для проектов средней сложности. В целом, эти инструменты схожи, и большинство отличий не являются критичными. Разработчики чаще выбирают фреймворк, основываясь на личных предпочтениях, удобстве синтаксиса или просто остановившись на том, что первым привлекло внимание.
Вчера мы говорили про отличия Vue и React, там упоминались "реактивные прокси", хочу описать что это такое.

🌟 Реактивные прокси в JavaScript: мощный инструмент для управления состоянием! 🚀

Если вы работаете с JavaScript и хотите создавать приложения с интерактивным интерфейсом, то реактивные прокси — это то, что нужно. Они позволяют отслеживать изменения в объектах и автоматически реагировать на них. Это основа таких библиотек, как Vue, React (через состояния), MobX и других.

Что такое реактивный прокси?
Это объект, обёрнутый в Proxy, который позволяет вам "шпионить" за его изменениями. С его помощью можно перехватывать чтение, запись и удаление свойств, а также выполнять дополнительные действия, такие как обновление интерфейса.

Как это работает? 🔧
Пример создания реактивного объекта:

const state = {
count: 0,
};

const reactiveState = new Proxy(state, {
get(target, prop) {
console.log(`Чтение свойства: ${prop}`);
return target[prop];
},
set(target, prop, value) {
console.log(`Изменение свойства: ${prop}, новое значение: ${value}`);
target[prop] = value;
// Дополнительно: можно вызывать функции для обновления UI
return true;
},
});

reactiveState.count; // Лог: Чтение свойства: count
reactiveState.count = 5; // Лог: Изменение свойства: count, новое значение: 5


Зачем это нужно?
1. Отслеживание состояния. Прокси позволяет знать, что изменилось, и на основе этого реагировать.

2. Управление данными. Можно добавлять проверки, логику валидации или другие действия при изменении свойств.

3. Упрощение работы с UI. В таких библиотеках, как Vue или MobX, это ключевая концепция для автоматического обновления интерфейса.

Реактивность в популярных фреймворках
• Vue.js: Использует Proxy для создания реактивных данных. Например, reactive() и ref().
• MobX: Строит реактивные цепочки для отслеживания и автоматического пересчёта зависимостей.
• React: Реализует аналог реактивности через хуки (useState, useEffect), но без Proxy.

Важно помнить ⚠️
• Прокси работают только с объектами и массивами. Примитивы не поддерживаются напрямую.
• Следите за производительностью: большие объекты могут замедлить приложение.
В прошлом посте я упомянул, что React реализует аналог реактивности через хуки (useState, useEffect), но без Proxy и мне в личку полетели вопросы, сейчас хочу описать как это делает React.

В React реактивность реализуется через систему виртуального DOM и обновление состояний, но без использования Proxy.

1. Основные концепции React
useState: Позволяет хранить и изменять состояние компонента.
useEffect: Слушает изменения состояния или пропсов и выполняет побочные эффекты.
• React использует механизм сравнения (reconciliation), чтобы определить, какие части интерфейса нужно обновить.

2. Как работает реактивность в React?
Вместо перехвата изменений в объекте (как в Proxy), React применяет декларативный подход:

I. Изменение состояния через useState:
• Когда вы вызываете функцию setState (например, setCount), React получает сигнал о том, что состояние изменилось.
• React планирует перерендерить компонент (это называется re-rendering).

II. Ререндеринг компонента:
• React вызывает компонент-функцию заново.
• На основе нового состояния React генерирует новый виртуальный DOM.

III. Сравнение виртуальных DOM:
• React сравнивает предыдущую и новую версии виртуального DOM (использует алгоритм diffing).
• React обновляет только те части реального DOM, которые изменились.

3. Почему React не использует Proxy?
React делает ставку на контроль и предсказуемость:
Явные обновления через setState: В React вы должны явно вызывать функцию для изменения состояния (setState или setCount). Это упрощает отслеживание источников изменений.
Производительность: Использование Proxy для всех объектов может быть дорогостоящим, особенно если у вас большое состояние.
Детерминированность: React следит за тем, чтобы компоненты обновлялись только в ответ на заранее определённые изменения (через хуки или контекст).

4. Как React "знает", что нужно обновить?
• Когда вы вызываете setState или setCount, React добавляет обновление в очередь.
• После этого React вызывает "обновляющий цикл":
I. Обновляет состояние компонента.
II. Генерирует новый виртуальный DOM.
III. Сравнивает с предыдущим виртуальным DOM.
IV. Обновляет только изменившиеся элементы в реальном DOM.

5. Плюсы подхода React
Простота: Вы явно управляете состоянием.
Производительность: Алгоритм diffing оптимизирует обновления.
Прогнозируемость: Вы всегда знаете, что вызывает изменения.

Хотя Proxy предоставляет автоматическое отслеживание изменений, React предпочитает декларативный и контролируемый подход для обеспечения надёжности и предсказуемости в обновлениях интерфейса.
Многие не задумываются, но даже так можно делать… 🐱💻

Вот вам пример на JavaScript:

const originalPush = Array.prototype.push;

Array.prototype.push = function (...args) {
console.log('Элементы добавлены:', args);
return originalPush.apply(this, args);
};

const arr = [1, 2, 3];

arr.push(4, 5); // Выведет: Элементы добавлены: [4, 5]


🔥 Что происходит? Мы сохраняем оригинальный метод push, переопределяем его, чтобы добавить console.log, а затем вызываем оригинальный метод через apply.

⚡️ Это может быть полезно для отладки или отслеживания изменений массивов в вашем приложении. Но аккуратнее: изменение встроенных методов может привести к неожиданным багам в других местах вашего кода.
Архитектура — это фундамент любого программного проекта. Она определяет, как система будет развиваться, масштабироваться и поддерживаться. Ошибки на этом этапе могут привести к хаосу, увеличенным срокам и непредсказуемости. Именно поэтому архитектура — это самое главное в программировании.

Существует множество подходов к проектированию архитектуры:

• Микросервисная архитектура
• Монолитная архитектура
• Чистая архитектура (Clean Architecture)
• Feature-Sliced Design
• И многие другие.

Каждая из них подходит для разных задач, но лично я выбираю доменно-ориентированную архитектуру (Domain-Oriented Architecture, DOA).

🔑 Почему я выбираю DOA?

1. Четкие границы контекста (Bounded Context):
Система делится на независимые домены с четко определенными правилами, что предотвращает «эффект домино».

2. Фокус на бизнес-логике:
Архитектура строится вокруг потребностей бизнеса, а не технологий. Это позволяет создавать решения, которые действительно решают задачи заказчика.

3. Модульность и масштабируемость:
Каждый домен автономен, что упрощает тестирование, поддержку и развитие проекта.

4. Единый язык (Ubiquitous Language):
Использование общего языка между бизнесом и разработчиками помогает избежать недопонимания.

💡 Почему архитектура важна:

• Она задает основу для всей системы.
• Облегчает внедрение новых разработчиков в проект.
• Упрощает масштабирование и добавление новых функций.
• Делает систему предсказуемой и понятной.

Для меня DOA — это подход, который позволяет создавать системы, развивающиеся вместе с бизнесом. Такие системы не только устойчивы к изменениям, но и обладают важным преимуществом: итоговый продукт становится доступным и понятным для разработчиков любого уровня подготовки.
Про всплытие (или event bubbling) в JavaScript 🌊

Всплытие событий — это основополагающий механизм в браузерном JavaScript.

🔧 Как это работает?

1. При нажатии на элемент событие сначала срабатывает на нём (фаза захвата).

2. Затем оно поднимается кроме других объектов (фаза всплытия).

🎓 Пример:
<div id="parent">
<button id="child">Кликни меня!</button>
</div>

document.getElementById('parent').addEventListener('click', () => {
console.log('Клик на parent!');
});

document.getElementById('child').addEventListener('click', () => {
console.log('Клик на child!');
});


🔎 Что получим при клике на child?

1. Сначала выполнится console.log('Клик на child!');

2. Затем сработает console.log('Клик на parent!');

Вот и всплытие 👇

🏢 Когда это не нужно?
Иногда нам нужно остановить пропагацию события.

document.getElementById('child').addEventListener('click', (event) => {
event.stopPropagation(); // Прекращает всплытие
console.log('Теперь только child!');
});

Этот приём часто используют в сложных взаимодействиях.

Если остались вопросы про всплытие и погружение, пишите в комментарии! 🙋‍♂️
💡 preventDefault — как это работает?

Когда вы кликаете по ссылке или отправляете форму, браузер по умолчанию выполняет определённые действия. Например, переходит на другую страницу или перезагружает текущую. Но что, если вы хотите изменить это поведение? 🤔

👉 Метод event.preventDefault() останавливает стандартное действие браузера. Это полезно, если вы хотите реализовать свою логику при каком-либо событии.

Пример:
document.querySelector('a').addEventListener('click', (event) => {
event.preventDefault();
console.log('Клик перехвачен, но перехода не будет!');
});


🔥 Используйте preventDefault, чтобы:

• Сделать кастомную отправку формы через AJAX 📨
• Остановить переходы по ссылкам 📌
• Управлять кликами, свайпами и любыми действиями пользователя 🎮

Но не путайте с stopPropagation()! Этот метод блокирует всплытие событий, а preventDefault — только стандартное поведение.
Контекст провайдер в 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. Разделяйте контексты для разных данных
Если вы используете один контекст для хранения нескольких типов данных (например, тема, авторизация и настройки), то изменение любой из этих данных вызовет ререндер всех потребителей. Лучше создать отдельный контекст для каждой группы данных.

Пример:
// Тема
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. Подключение контекста через компонент-посредник
Если компоненту нужно только обновлять данные контекста, используйте специальный компонент, который изолирует изменения.
Пример:
const ThemeUpdater = () => {
const { toggleTheme } = useContext(ThemeContext);
return <button onClick={toggleTheme}>Сменить тему</button>;
};

Таким образом, ререндер затронет только ThemeUpdater, а не всю структуру.

Заключение
Чтобы избежать "ада ререндеров" при использовании контекста в React:

1. Разделяйте данные на несколько контекстов.
2. Используйте useMemo для мемоизации значений.
3. Подключайте контекст только в необходимых местах.
4. Используйте кастомные хуки и мемоизацию для работы с контекстом.

Применяя эти подходы, вы сможете эффективно управлять состоянием приложения и обеспечить высокую производительность.