Заметки про React – Telegram
Заметки про React
3.78K subscribers
34 photos
8 videos
485 links
Короткие заметки про React.js, TypeScript и все что с ним связано
Download Telegram
Когда React рендерит компонент

Чтобы эффективно создавать приложения на React, полезно понимать поведение рендеринга React и знать правила как избежать повторный рендер.

React рендерит компонент, если:
- у компонента запланировано обновление стейта.
- произошел рендер родительского компонента и текущий компонент не соответствует критериям отказа от повторного рендера, где все эти четыре условия должны выполняться одновременно:
- Компонент был уже отрендерен и смонтирован.
- Нет изменений пропсов.
- Нет изменений в значении контекста, который используется в компоненте.
- Сам компонент не запланировал обновление.

Существуют два типа рендеринга, которые могут произойти с компонентом:
- активный рендеринг:
- Компонент (или кастомный хук) заранее планирует обновления для изменения стейта.
- Прямой вызов ReactDOM.render.
- пассивный рендеринг: родительский компонент запланировал обновление стейта и текущий компонент не соответствует критериям отказа от повторного рендера (четыре условия выше).

https://www.zhenghao.io/posts/react-rerender
👍13
Как React 18 может сломать ваше приложение

Уже сейчас можно обновить библиотеку React до 18 версии, и разработчики утверждают, что обновление должно быть безболезненным. Однако, иногда после обновления могут происходить ошибки в приложениях. Связано это с новым режимом работы StrictMode.

В dev режиме React будет делать проверку, автоматически размонтировать и повторно монтировать каждый компонент всякий раз, когда компонент монтируется в первый раз, восстанавливая предыдущее состояние стейтов при втором монтировании. Это необходимо, чтобы в будущем React мог удалять и восстанавливать секции UI с сохранением стейта. Например, при переключении табов на странице. Как работают эффекты в таком режиме:

* React mounts the component.
* Layout effects are created.
* Effect effects are created.
* React simulates effects being destroyed on a mounted component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates effects being re-created on a mounted component.
* Layout effects are created
* Effect setup code runs


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

function useIsMounted() {
const isMountedRef = React.useRef(true);
React.useEffect(() => {
return () => {
isMountedRef.current = false;
};
}, []);
return () => isMountedRef.current;
}


Например, хук выше определяет, смонтирован ли компонент. В React 18 данный хук будет работать неправильно, т.к. при проверке стабильности эффектов React будет несколько раз запускать и очищать useEffect. Чтобы исправить хук, нужно добавить присвоение в самом эффекте:

React.useEffect(() => {
isMountedRef.current = true;
return () => {
isMountedRef.current = false;
};
}, []);
👍9
Изменения типов в React 18

Вышло мажорное обновление библиотеки типов для React 18 @types/react@^18.0.0. В нем есть несколько критических изменений:

- Убран неявный проп children в React.FC и React.Component. Теперь, если компонент ожидает проп children, то его нужно передавать явно:

interface Props {
children?: React.ReactNode;
}

const SomeFunctionComponent: React.FC<Props> = props => <div>{props.children}</div>

Есть несколько причин, почему был удален проп children. Во первых, очень часто этот проп не использовался. Но так как он был объявлен в типах, то это могло приводить к неверным предположениям о работе компонента (раз есть проп children, то значит он как-то используется внутри компонента). Во вторых, более строгая типизация пропа children, теперь можно самостоятельно указать его тип. Более подробно про причины удаления неявного children написано здесь.

- Убран тип {} у ReactFragment. Это никогда не было правильным и в основном требовалось для взаимодействия с неявными пропом children.

- this.context по умолчанию станет unknown. Раньше по умолчанию был any. Это распространяется и на функцию useCallback, теперь, если тип не указан, то по умолчанию аргумент будет unknown.

- Удалены неиспользуемые типы. Приведены к общей терминологии, как они называются в репозитории и документации:

-StatelessComponent
+FunctionComponent

-SFC
+FC

-React.ReactType
+React.ElementType

-SFCElement
+FunctionComponentElement

// без замены
-Props
-RefForwardingComponent
-SFCFactory

https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210
👍12
Композиция компонентов в React: как сделать это правильно

При разработке приложения на React возникает сложность в разделении компонентов и их правильной композиции между собой. Некоторые разработчики не разделяют компонент на более мелкие и получают большой компонент-монолит, который в дальнейшем сложно поддерживать. Также некоторые разработчики начинают разделять компонент на более мелкие подкомпоненты слишком рано, что приводит к оверинженирингу и сложно поддерживаемому коду.

В React есть несколько подходов к композиции компонентов. Есть “простые” компоненты:

const Button = ({ noscript, onClick }) => <button onClick={onClick}>{noscript}</button>;

И компоненты контейнеры:

const Button = ({ children, onClick }) => <button onClick={onClick}>{children}</button>;

У компонента контейнера вместо пропса noscript проп children. Это различие позволяет более гибко использовать компонент контейнер и передавать в children любые элементы, при этом синтаксис выглядит как использование обычного HTML тега.

Можно определить следующие правила для декомпозиции компонента:

- Начинайте реализацию “сверху вниз”. Если делать “снизу вверх”, т.е. сначала создавать небольшие переиспользуемые компоненты, то возможно у вас получится компонент со слишком сложным API и будет отсутствовать половина нужного функционала.

- Создавайте подкомпонент только тогда, когда в этом есть необходимость. Одно из возможных причин, когда компонент нужно декомпозировать – это его размер.

- Начинайте композицию с “простых” компонентов, а другие подходы композиции используйте по мере необходимости.

Также стоит отметить про принцип Единый Уровень Абстракции (Single Level of Abstraction).

https://www.developerway.com/posts/components-composition-how-to-get-it-right
👍6🔥6👎1
Упускаемый из виду фактор оптимизации производительности в React

Когда идет речь о производительности компонента, необходимо учитывать его положение в дереве компонентов.

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

Наверняка вы знаете поведение рендера при использовании контекста: при изменении значения контекста компонент-потребитель будет отрендерен заново (проверка происходит по ссылке).

function App() {
return (
<Parent>
<Child />
</Parent>
);
}

function Parent({children}) {
const [stateA, dispatchA] = useReducer(reducerA, initialStateA);

return (
<div className="parent">
<Context.Provider value={[stateA, dispatchA]}>{children}</Context.Provider>
</div>
);
}

В примере выше при каждом повторном рендере Parent будет происходить повторный рендер потребителей контекста, т.к. изменилась ссылка на значение. Самый легкий способ это исправить - это обернуть значение в useMemo. Однако это избыточно для компонентов в верху дерева компонентов. В примере выше Parent находится в самом верху дерева и выше него нет других компонентов, которые могли бы спровоцировать повторный рендер. Таким образом использование функции useMemo в примере выше бесполезна.

https://www.zhenghao.io/posts/top-level-perf
👍6
История Next.js

На канале uidotdev вышло видео про Next.js, в котором рассказывают про историю появления этого фреймворка и какие проблемы он решает. Также, в видео есть немного про историю JavaScript.

https://www.youtube.com/watch?v=BILxV_vrZO0
👍5
Производительность фронтенда: большое приложение на реактивном SSR-топливе

Виталий Старов рассказывает как устроен серверный рендеринг в большом приложение. В статье описан путь пользователя при загрузке страницы сайта, какие процессы стоят за этим действием. Статья хороша тем, что объясняется выбор определенных библиотек.

Одна из проблем в серверном рендеринге – стили. Если использовать CSS-in-JS, то могут возникнуть проблемы при гидратации. Например, JSS наиболее чувствителен к ситуации, когда на сервере и в браузере компонент был отрендерен по разному, в этом случае стили могут сломаться.

https://habr.com/ru/company/superjob/blog/660681/
👍3
Новый текстовый редактор для React – Lexical

Lexical – это новая библиотека для создания текстового редактора с упором на надежность, доступность и производительность. Ядро библиотеки весит всего 22КБ, а нужные плагины для работы редактора можно загружать через lazy load по мере их использования пользователем.

Библиотека не привязана к какому-то конкретному фреймворку и даже к платформе. В планах есть реализация API для iOS. В первую очередь был выбран web и есть официальные биндинги для React.

Внутреннее состояние редактора представляет из себя дерево узлов и объект выбора. Узлы представляют собой основную концепцию в Lexical. Они не только нужны для рендера, но также представляют базовую модель данных для хранения в редакторе для истории. Узлы в редакторе расширяемы, и на основе существующих можно создавать свои кастомные узлы.

Пример компонента:

const editorConfig = {
// The editor theme
theme: ExampleTheme,
// Handling of errors during update
onError(error) {
throw error;
},
// Any custom nodes go here
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode
]
};

export default function Editor() {
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<ToolbarPlugin />
<div className="editor-inner">
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
/>
<HistoryPlugin />
<TreeViewPlugin />
<AutoFocusPlugin />
<CodeHighlightPlugin />
<ListPlugin />
<LinkPlugin />
<AutoLinkPlugin />
<ListMaxIndentLevelPlugin maxDepth={7} />
<LexicalMarkdownShortcutPlugin />
</div>
</div>
</LexicalComposer>
);
}


https://lexical.dev/
🔥6👍1
Хуки, зависимости и устаревшие замыкания

Замыкание очень часто используется в функциональных компонентах, когда внутри компонента объявляется функция и в ней используются пропсы компонента:

function Counter({ count }) {
const logCount = () => {
// переменной count берется из внешнего окружения
console.log('count', count)
}

return <button onClick={logCount}>Show me the count</button>
}

Замыкание- это когда функция может взаимодействовать с окружающими переменными. Само замыкание создается каждый раз, когда создается функция, в момент создания функции.
В функциональных компонентах замыкание работает как надо, при каждом рендере компонента объявленные внутри компонента функции создаются заново и получают в свое окружение новые пропсы.

Чаще всего это не вызывает никаких проблем, однако, если понадобится мемоизация функции, то нужно будет оборачивать функцию в useCallback.
При работе с хуками рекомендуется использовать линтер react-hooks/exhaustive-deps, который подсвечивает неопределенные зависимости для хука useCallback. Если игнорировать список зависимостей useCallback, то используемые внутри него переменные компонента будут устаревшими.

https://tkdodo.eu/blog/hooks-dependencies-and-stale-closures
👍7
HolyJS — конференция для JavaScript-разработчиков от JUG Ru Group, в формате online+offline

Online-часть: 8–10 июня.
Offline-день: 23 июня.

В online вас ждут выступления, посвященные трендам и новым технологиям, обмен опытом и общение в чатах.
На offline-дне в Санкт-Петербурге можно вживую послушать спикеров, лично познакомиться с коллегами и потусоваться у стендов партнеров.

В программе:
– Каким бывает легаси во фронтенде, как с ним справляться и не допускать его появления.
– Что такое proposal Wasm GC, почему его так долго делают и какой путь прошли движки за 2 года экспериментов.
– Как построить дизайн-систему для продукта с 20-миллионной аудиторией, огромной кодовой базой и массой поверхностей.
– Что такое сложность для человеческого мозга и как эти знания помогают писать более понятный код.

Для подписчиков канала организаторы сделали промокод, который поможет вам купить персональный билет со скидкой: reactnotes2022JRGpc

Билеты, подробности и первая программа— на сайте.
👍31
Вышло обновление React 18.1

Спустя месяц после релиза вышло минорное обновление React 18.1.

Среди основных изменений:
- тег ​​<noscript> больше не ругается на react-dom/client
- renderToPipeableStream стал в ~20 раз быстрее
- исправлено двойное срабатывание componentWillUnmount в Suspense
- исправлена ошибка бесконечного ререндера компонента в useDeferredValue
- исправлены различные ошибки гидратации

https://github.com/facebook/react/blob/main/CHANGELOG.md#1810-april-26-2022
🔥15
Как работает Virtual DOM в React

Как мы знаем, DOM- это древовидная структура, которая используется для отображения элементов на странице. Работа напрямую с DOM может быть достаточно дорогостоящим процессом, т.к. операции с DOM выполняются медленно. React работает не напрямую с браузерным DOM, а через Virtual DOM.

Virtual DOM – это древовидная структура объектов, которая хранится в памяти браузера и хранит элементы страницы. Задача Virtual DOM – определить, что изменилось с точки зрения содержимого элементов или структуры страницы и закоммитить все изменения в реальный DOM.

По своей сути Virtual DOM – это древовидная структура объектов FiberNode (а в целом это FiberTree). FiberNode представляет собой React элемент.

function App () {
return (
<article>
<h2>Title</h2>

<p>Some content</p>
</article>
)
}

Компонент выше содержит четыре FiberNode: App, <article>, <h2>, <p>. Каждый FiberNode содержит ссылки на соседние FiberNode, родительский и дочерний. Рассмотрим основные свойства FiberNode, сопоставив их значения с примером выше:
- child - дочерний элемент. AppFiberNode.child === ArticleFiberNode
- sibling - соседний элемент. H2FiberNode.sibling === PFiberNode
- return - родительский элемент. H2FiberNode.return === ArticleFiberNode

В процессе рендера для того, чтобы узнать о необходимых изменениях, в каждом FiberNode есть свойство alternate. В нем хранится ссылка на копию текущего FiberNode, который был закоммичен в DOM. В процессе рендера происходит сравнение текущего FiberNode и его копии в alternate, и, если они отличаются, то происходит коммит изменений.

https://indepth.dev/posts/1501/exploring-how-virtual-dom-is-implemented-in-react
👍62
RFC useEvent

В React RFC появилось новое предложение от Дэна Абрамова – добавить хук useEvent. Этот хук будет предназначен для использования в обработчиках событий.

function Chat() {
const [text, setText] = useState('');

const onClick = useEvent(() => {
sendMessage(text);
});

return <SendButton onClick={onClick} />;
}

Код внутри useEvent будет иметь доступ к актуальным значениям пропсов/стейтов в момент вызова. Возвращаемая функция будет иметь одинаковую ссылку, даже если пропсы/стейты, на которые она ссылается, изменились. Также в useEvent не будет массива зависимостей.

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

function Page({ route, currentUser }) {
// Stable identity
const onVisit = useEvent(visitedUrl => {
logAnalytics('visit_page', visitedUrl, currentUser.name);
});

useEffect(() => {
onVisit(route.url);
}, [route.url]); // Re-runs only on route change
// ...
}

Этот хук похож на useCallback, только без массива зависимостей. Однако, есть ограничения на его использование. Например, его нельзя использовать для рендеринга компонентов (т.е. возвращать JSX).

RFC https://github.com/reactjs/rfcs/blob/useevent/text/0000-useevent.md
Обсуждение https://github.com/reactjs/rfcs/pull/220
👍15
Использование пропа key в React для эффективного рендера

В документации React для лучшей производительности списков советуют использовать в качестве key уникальный id элемента. Но что будет, если использовать в качестве key текущий индекс элемента?
Чтобы разобраться с этим, давайте изучим, как работает проп key. Если обобщенно, то он используется для идентификации элементов одного типа в списке при ререндере элементов.

Например, есть два списка элементов:

const ItemMemo = React.memo(Item);

const CountriesList = ({ countries }) => {
const [sort, setSort] = useState('asc');
const sortedCountries = orderBy(countries, 'name', sort);
const button = <button onClick={() => setSort(sort === 'asc' ? 'desc' : 'asc')}>toggle sorting: {sort}</button>;

return (
<div>
{button}
{sortedCountries.map((country) => (
<ItemMemo country={country} key={country.id} />
))}
{sortedCountries.map((country, index) => (
<ItemMemo country={country} key={index} />
))}
</div>
);
};

В качестве триггера для ререндера будет использоваться кнопка, которая меняет сортировку элементов.

В случае, когда в key используется текущий индекс, при ререндере списка у первого элемента всегда будет key = 0. React считает элементы одинаковыми, если у них одинаковый key. Поэтому при использовании в качестве key текущий индекс массива, элементы списка всегда будут считаться одинаковыми. Но проп country у элементов будет другой, поэтому будет происходить ререндер мемоизированного компонента.
Для списка, где в key используется id, при ререндере элементов React идентифицирует одинаковые элементы и не происходит ререндера элементов.

https://www.developerway.com/posts/react-key-attribute
👍8
React для создания видео

Remotion – инструмент для создания видео на React. Зачем нужно создавать видео, используя React?
- Используйте веб-технологии: CSS, WebGL, Canvas и т.д.
- Используйте программирование: переменные, функции, API и различные алгоритмы.
- Используйте React: переиспользование компонентов, композиция.

Remotion – это как After Effects, но для программистов. Если в After Effects потребуется переиспользовать какой-то элемент в нескольких местах, то придется копировать его в ручную. В Remotion используется React, где хорошо работает композиция и можно легко переиспользовать любой элемент.

Один из плюсов данного инструмента в том, что можно создавать видео на лету, кастомизируя его содержимое. Например, используя данные из API создавать индивидуальное видео для каждого пользователя.

Основная идея Remotion в том, что компонент получает текущий фрейм видео. Компонент что-то рендерит, Remotion это записывает и создаёт видео. Для создания скриншота рендера фрейма используется Puppeteer, запускаемый в параллельном режиме. Для склейки скриншотов и создания видео используется FFMPeg.

import {interpolate, useCurrentFrame} from 'remotion'

const Title: React.FC<{noscript: string}> = ({noscript}) => {
const frame = useCurrentFrame()
const opacity = interpolate(frame, [0, 20], [0, 1], {extrapolateRight: 'clamp'})

return (
<div style={{opacity}}>{noscript}</div>
)
}

export const MyVideo = () => {
return (
<div style={{ textAlign: "center" }}>
<Title noscript="Hello World" />
</div>
);
};


https://www.remotion.dev/
Интервью с мейнтенером https://www.learnwithjason.dev/make-video-with-code
👍6
Как работает поверхностное сравнение в React

Поверхностное сравнение используется в React в качестве механизма для определения необходимости обновления компонента, массива зависимостей хука, изменения пропсов мемоизированного компонента. Давайте рассмотрим функцию поверхностного сравнения и разберемся как она работает.

import is from './objectIs';
import hasOwnProperty from './hasOwnProperty';

function shallowEqual(objA: mixed, objB: mixed): boolean {
/**
* Проверка равенства примитивных значений.
*/
if (is(objA, objB)) {
return true;
}

/**
* Проверка на то, что текущие аргументы являются объектами и не равны null.
*/
if (
typeof objA !== 'object' ||
objA === null ||
typeof objB !== 'object' ||
objB === null
) {
return false;
}

/**
* На этом шаге мы уверены что работаем с объектами, получаем ключи объекта.
*/
const keysA = Object.keys(objA);
const keysB = Object.keys(objB);

/**
* Сравниваем количество ключей объектов.
*/
if (keysA.length !== keysB.length) {
return false;
}

/**
* Сравнение значения объекта для каждого ключа объекта A с объектом B.
* Проверяется наличие ключа объекта A в объекте B.
*/
for (let i = 0; i < keysA.length; i++) {
const currentKey = keysA[i];
if (
!hasOwnProperty.call(objB, currentKey) ||
!is(objA[currentKey], objB[currentKey])
) {
return false;
}
}

return true;
}


Выводы:
- Для сравнения объектов используется Object.is вместо строгого равенства.
- В поверхностном сравнение пустой массив и пустой объект равны.
- Объект с индексами в ключах и массив с такими же значениями одинаковы, т.е. { 0: 2, 1: 3 } равен [2, 3].
- Из-за использования Object.is вместо ===, +0 и -0 не равны, а Number.NaN и NaN равны.

https://www.chakshunyu.com/blog/how-does-shallow-comparison-work-in-react/
👍4
useTilg – хук для отладки компонентов

Tiny Logger – небольшой хук для отладки компонентов. Выводит информацию, когда компонент был отрендерен (с какими пропсами), смонтирован и размонтирован. Также хук позволяет отследить как изменились стейты при рендере.

import { useState } from 'react'
import useTilg from 'tilg'

function MyApp() {
const [input, setInput] = useState('')
const [count, setCount] = useState(0)

useTilg()`input = ${input}, count = ${count}`

return (
<>
<input onChange={(e) => setInput(e.target.value)} value={input} />
<button onClick={() => setCount(count + 1)}>{count}</button>
</>
)
}


https://github.com/shuding/tilg
👍10👎2
Почему нельзя вызывать хуки по условию

Например, компонент по определенному условию вернул null. Это невалидный код на React. Но почему так делать нельзя?

const ConditionalComponent = (props) => {
if (!props.visible) return null;

const [value, setValue] = useState();
}


В первую очередь стоит помнить, что babel компилирует код JSX в обычные функции createElement:

const MyComponent = () => {
return <div>Hello world</div>
}

// babel компилирует в
const MyComponent = () => {
return React.createElement("div", null, "Hello world");
};

React рендерит компоненты, вызывает createElement, создает Virtual DOM и выводит пользователю в браузер отрендеренные элементы. Рендер – это просто вызов функции функционального компонента. Функциональный компонент вызывается, возвращается вызов createElement, React сравнивает текущее и новое дерево VDOM и коммитит изменения в DOM.

Важно понимать, что функциональный компонент это функция, которую вызывает React. Это создает проблему: как сохранить данные в стейте при вызовах функции (рендере). Например, следующий код при каждом вызове будет писать в консоль “1”:

const Test = () => {
let a = 1;
console.log(a);
a++;
}

Test(); // 1
Test(); // 1
Test(); // 1


Для того чтобы сохранять состояние стейта между рендерами компонента, React сохраняет состояние во внешнем объекте по ID хука, примерно так:

const state = [];

// Уникальный ID хука, счетчик
let idx = 0;

function useState(init) {
state[idx] = state[idx] ?? { val: init };

return [
state[idx].val,
data => state[idx].val = data
]
}

function Test() {
const [name, setName] = useState('React');
}

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

Более подробно про хуки написано в блоке Дэна Абрамова: https://overreacted.io/why-do-hooks-rely-on-call-order/
👍7
RFC шаблонов в Next.js

Next.js планирует изменить свою систему роутинга. Это поможет адаптировать фичи React 18 и другие новые фичи.

Добавятся новые конвенции именования. Папка ./pages останется, но появится папка ./app, в которой будут работать новые фичи, например, Server Components.

Появится поддержка вложенных роутов/шаблонов с использованием компонента layout.js. Также появится компонент page.js, который будет использоваться для отображения страницы роута.

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

https://nextjs.org/blog/layouts-rfc
👍4🔥3
Мигрируем с Jest на Vitest

Vitest – это фреймворк тестирования, использующий в качестве сборщика Vite.js. Vitest использует совместимый с Jest API и поддерживает из под коробки основные фичи для тестирования (моки, снапшоты, покрытие). Также фреймворк поддерживает многопоточность, используя библиотеку tinypool.

Vitest особенно актуален, если вы используете в качестве сборщика приложения Vite.js. В этом случае для запуска тестов и сборки приложения будет использоваться один и тот же конфиг приложения (vite.config.js).

Если вы используете Jest, то совсем не сложно перейти на Vitest, учитывая что их API похоже. Катал Мак Доннача описал шаги для миграции с Jest на Vitest:

https://cathalmacdonnacha.com/migrating-from-jest-to-vitest
👍3👎1
Обзор библиотек управления состоянием

Библиотеки управления состоянием определяют, как приложение будет делиться своим состоянием. Оно может быть глобальным или локальным для определенного участка приложения.

Менеджеры состояний позволяют избавиться от проп-дриллинга, когда компоненты создают пропсы, чтобы передать их в дочерние компоненты.

Альберт Гао в своем блоге составил обзор популярных библиотек управления состоянием. Библиотеки разбиты на категории: по структуре сторов (глобальный, несколько или атомарный), способу чтения записей из стора (селектор или геттер) и способу обновления стора (React-подобный API, экшен объект или инкапсулированный метод стора).

https://www.albertgao.xyz/2022/02/19/react-state-management-libraries-2022/
👍51