React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
Redux Essentials 22. RTK Query + EntityAdapter

Возвращаемся к EntityAdapter в userSlice. Все оказалось очень просто: при получении данных запроса в transformResponse просто вызываем метод usersAdapter.setAll, чтобы сохранить в нужном виде.

https://codesandbox.io/s/redux-essentials-rtk-query-5-react-junior-b5ebze?file=/src/features/users/usersSlice.js

const usersAdapter = createEntityAdapter();
const initialState = usersAdapter.getInitialState();

getUsers: builder.query({
// ...
transformResponse: function (responseData) {
return usersAdapter.setAll(initialState, responseData);
}
})

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

// получение данных запроса из apiSlice
export const selectUsersResult = getUsersEndpoint.select();

// извлечение данных из запроса, если он выполнен
// результат работы entity adapter ({ids, entities}) или undefined
const selectUsersData = createSelector(
selectUsersResult,
function (usersResult) {
return usersResult.data;
}
);

export const {
selectAll: getUsers,
selectById: getUserById
} = usersAdapter.getSelectors(
function(state) {
return selectUsersData(state) ?? initialState;
}
);

#redux #управлениесостоянием #обменданными #rtkquery #документация #примерыкода
👍2
RTK Query. Query hook: параметры

В хук запроса, сгенерированный для эндпоинта, можно передать параметры:

- queryArg (параметр для генерации урла запроса, передается в функцию query)
- queryOptions (объект с настройками).

Настройки есть следующие:

- skip - пропустить выполнение запроса для текущего рендера.
- pollingInterval - для автоматического перезапроса данных.
- selectFromResult - позволяет изменить возвращаемое хуком значение (оптимизировано для рендера).
- refetchOnMountOrArgChange
- refetchOnFocus - refetch при возвращении фокуса на вкладку браузера.
- refetchOnReconnect - refetch при восстановлении сетевого соединения, если оно было потеряно.

#обменданными #rtkquery #документация
👍1
Redux Essentials 23. Выбор данных из запроса

Теперь займемся страницей пользователя, на которой выводятся отфильтрованные по автору посты.

https://codesandbox.io/s/redux-essentials-rtk-query-6-react-junior-kfzw2t?file=/src/features/users/UserPage.js

Воспользуемся хуком useGetPostsQuery, чтобы получить полный список постов и настройкой selectFromResult (параметры хука запроса), чтобы отфильтровать статьи по автору.

const { postsForUser } = useGetPostsQuery(undefined, {
selectFromResult: function(result) {
return {
...result,
postsForUser: (result.data || []).filter(function(post) {
return post.user === userId
})
}
}
});


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

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
Трансформация данных запроса: 3 подхода

Мы получаем данные из API в одном виде, но для использования может потребоваться их изменение. Сделать это можно в трех местах:

1. Сразу после получения. Преобразуем ответ с помощью transformResponse и сохраняем в кеше уже измененные данные. Имеет смысл, если новый формат подходит всем потребителям. Можно сочетать с entity adapter.

2. В компоненте. Получаем данные, сохраненные в кеше, и извлекаем из них то, что нужно. Рекомендуется использовать useMemo для избежания ненужных перерендерингов. Полезно, если одному компоненту нужны данные в другом формате.

3. В хуке запроса. Отбираем нужные данные с помощью опции selectFromResult. Полезно, если компоненту нужна только часть данных.

#redux #rtkquery #обменданными #паттерны
👍2
Redux Essentials 24. RTK Query. Оптимистичные обновления

Переходим к реакциям на статьи.

https://codesandbox.io/s/redux-essentials-rtk-query-7-react-junior-uzedjr?file=/src/features/api/apiSlice.js

Добавляем эндпоинт-мутацию addReaction.

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

addReaction: builder.mutation({
query: function({ postId, reaction }) {},
invalidatesTags: function(result, error, arg) {
return [
{ type: 'Post', id: arg.postId }
];
}
})


Но ради такого мелкого изменения не хочется по новой запрашивать целый список. Для таких ситуаций можно воспользоваться техникой "оптимистичного обновления".

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

Для эндпоинта можно указать метод onQueryStarted. Это функция, которая при вызове получит два параметра - arg и thunkApi, в котором нас интересуют два метода - dispatch для отправки экшена и queryFulfilled - промис отправленного запроса.

Таким образом, запрос отправляется, но мы не хотим ждать его результата, а хотим внести изменения немедленно. У apiSlice есть свойство utils, а в нем метод updateQueryData. Он позволяет изменить ранее сохраненные данные. Для этого нужно указать название эндпоинта, ключ кеширования (если нужно) и собственно функцию для обновления.

const patchThunk = apiSlice.util.updateQueryData('getPosts', undefined, function(draft) {
// ... вносим изменения в данные draft
// данные можно мутировать
});


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

const patchResult = thunkApi.dispatch(patchThunk);


Если же запрос на сервер (queryFulfilled) не выполнится, патч можно отменить, вызвав метод patchResult.undo().

Осталось только внести изменения в компонент ReactionButton, чтобы он использовал хук новой мутации.

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
Redux Essentials 25. RTK Query. Потоковое обновление кеша

И наконец, осталось только переделать уведомления.

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

https://codesandbox.io/s/redux-essentials-rtk-query-7-react-junior-uzedjr?file=/src/features/notifications/notificationsSlice.js

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

Создание эндпоинта

Создадим новый эндпоинт getNotifications, который будет получать исходный список. Для удобства вынесем его в файл notificationsSlice.js и подключим с помощью метода apiSlice.injectEndpoints.

Экспортируем хук useGetNotificatinsQuery и используем его в компоненте NotificationsList. Тут ничего нового.

Подписка на новые уведомления

У эндпоинта есть метод жизненного цикла onCacheEntryAdded. Он вызывается в тот момент, когда эндпоинт добавляется в кеш, то есть для него "бронируется" там место (данные при этом еще не получены).

Внутри этого метода мы можем подписаться на сокет-канал.

Метод принимает два параметра: arg и thunkApi. В последнем есть несколько полезных методов и данных:

const {
updateCachedData,
cacheDataLoaded,
cacheEntryRemoved,
dispatch
} = thunkApi;


Последовательность действий:

- ждем, пока выполнится основной запрос (cacheDataLoaded)
- подписываемся на сокет-канал, при получении новых уведомлений добавляем их к кешированным ранее данным (updateCachedData)
- отслеживаем удаление кеша (cacheEntryRemoved), закрываем канал

Непрочитанные уведомления

Теперь список уведомлений хранится в apiSlice. Но нам еще нужно хранить их состояние (непрочитанные/прочитанные). Используем для этого уже существующий notificationsSlice, только вместо самих уведомлений поместим туда данные об их состоянии.

При добавлении новых уведомлений в кеш, нужно добавлять их и в notificationsSlice. Добавление происходит при выполнении базового запроса (экшен extendedApi.endpoints.getNotifications.matchFulfilled), а также при получении новых данных через сокет. Для второго случая создадим отдельный экшен notificationsReceived (используем утилитарную функцию createAction).

В extraReducers отслеживаем нужные экшены (builder.addMatcher) и добавляем новые элементы в список.

Осталось только в компоненте NotificationsList получить состояние уведомлений и отметить их как нужно.

#redux #управлениесостоянием #rtkquery #обменданными #документация #примерыкода
👍1
RTK Query. Резюме

Небольшое подведение итогов по RTK Query после прохождения руководства.

RTK Query работает поверх Redux Toolkit, используя его основные концепции.

Предполагается, что вся работа с API в проекте будет собрана в отдельный слайс, созданный с помощью функции createApi. Тут можно настроить, как именно должен выполняться запрос (`baseQuery`), а также указать набор эндпоинтов (`endpoints`).

Эндпоинт может быть запросом (query) или мутацией (mutation).

У каждого эндпоинта есть поле query, формирующее урл запроса. Полученный ответ можно обработать в методе transformResponse. Кроме того, можно форматировать данные прямо в компоненте (три подхода к форматированию данных)

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

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

Каждый эндпоинт можно пометить тегом (строка или объект), который можно инвалидировать (принудительно сбросить кеш и вызвать перезапуск запроса) в другом эндпоинте (уточненные теги).

Для эндпоинта генерируется хук (хуки запросов https://yangx.top/react_junior/342), который можно использовать в компонентах. Результат работы хука помимо данных содержит информацию о статусе запроса (`isFetching`, isLoading, `isError`). Кроме того, хук может принимать параметры для автоматического перезапроса данных и для выборки нужных данных из кеша (`selectFromResult`) (параметры хука запроса).

Можно работать с эндпоинтами и без хуков, они предоставляют ряд методов (`initiate()`, `select()`) (ручная работа с эндпоинтами).

RTK Query поддерживает оптимистичные обновления кеша, для этого используем метод жизненного цикла onQueryStarted и метод обновления apiSlice.util.updateQueryData.

Кроме того, возможно обновление кеша "по необходимости", например, при подписке на сокет-канал (потоковое обновление кеша). Для этого используем метод жизненного цикла onCacheEntryAdded.

#redux #rtkquery #обменданными
👍4
Forwarded from Cat in Web
Шахматы на React и TypeScript С НУЛЯ. Практикуем ООП

Видео (рус.): https://www.youtube.com/watch?v=mUvYGUYMvKo

Часовое (1 час 12 минут) руководство по созданию шахмат на React. Все последовательно и очень понятно, даже если вы (как я) не особенно близко знакомы с TypeScript. Если не планируете повторять, можно смотреть на скорости 1.5.

Логика игры максимально отделена от представления, поэтому вместо React ее можно реализовать на чем угодно.

Модели

👉 Класс Board (игровая доска) создает игровое поле и расставляет фигуры.
👉 Класс Cell (клетка доски) имеет цвет, координаты и знает, какая фигура на ней находится
👉 Класс Figure (фигура) и 6 дочерних классов для отдельных фигур хранят логику передвижения фигур по доске.

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

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

Мое повторение за автором: https://codesandbox.io/s/checkmate-react-ts-o8ob16?file=/src/models/Player.ts

#react #video #typescript
👍1
7 расширений VS Code, которые стоит знать разработчику React

Статья: https://nuancesprog.ru/p/15719/

1. React Style Helper

Упрощает написание стилей в JSX, поддерживает препроцессоры, есть автодополнение и go-to-definition.

2. VS Code React Refactor

Полезное приложение для рефакторинга, например, может извлекать фрагмент в JSX в отдельный компонент.

3. React PropTypes Intellisense

Находит PropTypes компонента и предлагает их для автодополнения.

4. ES7+ React/Redux/React-Native snippets

Полезные сниппеты.

5. Git Lens

Множество удобных функций для работы с историей изменений. Полезно любому разработчику.

6. Color Highlight

Находит hex-коды цветов и подсвечивает их нужным цветом.

7. React Documentation

Отображает подсказки из встроенной документации для методов и свойств.

#инструменты #ссылки
👍5
Forwarded from Cat in Web
Событийный цикл: микрозадачи и макрозадачи

Оригинал из learn.javascript: https://learn.javascript.ru/event-loop

Прекрасная статья из прекрасного учебника про основную концепцию JavaScript: цикл событий.

Цикл событий состоит из итераций, потому он и цикл. Каждая итерация начинается с выполнения "макротаска". Например, написанный вами синхронный код - это макротаск, и цикл событий начинается с его выполнения.

Когда макротаск выполнен, проверяется очередь микротасков. Это в основном промисы, а точнее блоки .then, .catch, .finally, которые готовы выполниться. Тело промиса выполняется синхронно, то есть входит в макротаск.

Итак, сначала выполняется ваш синхронный код, а затем все готовые промисы. Причем, все сразу, так что у них получается один контекст выполнения.

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

После этого начинается следующая итерация цикла событий. Для нее берется новый макротаск. Но откуда он берется, если весь код уже выполнен? Это различные асинхронные действия (кроме промисов). Это может быть выполнившийся setTimeout, пришедший ответ сервера или пользовательское событие, например, клик. При этом макротаск берется самый старый, который ждет дольше всех.

После того как он выполнится, снова запускаются все микротаски, которые готовы к этому моменту. И лишь после этого выполняется рендеринг - внесение изменений на страницу.

Итак, одна итерация цикла событий:
- один макротаск (самый старый из очереди) - синхронный код, setTimeout, ответы API, пользовательские события
- все готовые микротаски (обработчики промисов)
- рендеринг

После статьи есть задачки на понимание последовательности выполнения операций - несложные и полезные.

#javascript #eventloop #основныеконцепции
👍4
Вещи, которые полезно знать о React.js

Статья (рус.): https://habr.com/ru/post/679452/

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

1. Функция сброса в useEffect срабатывает при каждом перерендеринге компонента, а не только при размонтировании. Она объединяет в себе функциональность методов componentDidUpdate и componentWillUnmount.

2. Весь код внутри функционального компонента вызывается при каждом перерендере. Чтобы этого не происходило, нужно использовать хуки useEffect (для сайд-эффектов) и useMemo (для сложных вычислений) с указанием зависимостей.

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

4. Метод для изменения состояния, полученный из useState, может принимать функцию. Ее следует использовать, если новое состояние зависит от предыдущего.

5. Кроме useEffect есть еще useLayoutEffect, который отрабатывает синхронно и позволяет избежать мигания при манипуляциях с DOM.

#ссылки #оптимизация #хуки
👍6
Redux: динамическое добавление редьюсеров

Чтобы иметь возможность разделять код и подгружать что-то по мере необходимости (code splitting), нам нужна возможность динамически добавлять редьюсеры в хранилище. То есть при подгрузке нового функционала, нужно добавить новый редьюсер для работы с ним.

Основной принцип заключается с использовании метода хранилища store.replaceReducer. Как следует из названия он заменяет старый корневой редьюсер на новый.

При добавлении нового редьюсера мы должны объединить его с уже добавленными с помощью combineReducers, а затем заменить полученным результатом старый корневой редьюсер.

В документации описано два подхода реализации этого механизма:

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

#документация #управлениесостоянием #redux #оптимизация
👍2🤩1
Совместное использование данных разными редьюсерами

Для удобства мы обычно разделяем состояние на отдельные поля и создаем свой редьюсер для каждого (слайсы). Функция combineReducers объединяет их в один корневой редьюсер. При поступлении экшена каждый слайс-редьюсер работает со своей частью состояния.

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

В документации есть отдельная глава с разбором этой ситуации: Beyond combineReducers

Тут есть несколько вариантов реализации:

- обработка экшенов переносится в корневой редьюсер, который вызывает слайс-редьюсеры со всеми необходимыми аргументами
- в объект экшена добавляется все необходимое состояние
- добавление еще одного редьюсера поверх combineReducers с обработкой отдельных случаев

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

#управлениесостоянием #документация #redux
👍1
Переиспользование логики в редьюсерах

Статья из документации: Reusing Reducer Logic

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

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

Полезно посмотреть, чтобы расширить представление о том, что можно делать с редьюсерами.

#управлениесостоянием #документация #redux
👍1
Иммутабельное обновление состояния: паттерны

Статья из документации (англ.): Immutable Update Patterns

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

Речь идет о глубоком копировании объектов на всех уровнях (не только на первом). К счастью, у нас есть spread-оператор, который делает это удобнее.

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

Кроме того, перечислены некоторые утилитарные инструменты.

#управлениесостоянием #документация #redux
👍1
Повторение по селекторам

Статья в документации: https://redux.js.org/usage/deriving-data-selectors

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

Зачем нужны селекторы

Использование селекторов позволяет сохранять состояние максимально чистым (все, что можно вычислить, вычисляется в селекторах), а также дает возможность переиспользовать логику выбора данных (один селектор можно использовать в нескольких местах). Это особенно удобно, если селектор достает данные из глубин состояния (третий-четвертый уровень вложенности).

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

Организация селекторов

Документация рекомендует давать функция-селекторам осмысленные имена, начинающиеся с select.

Селекторы обычно располагаются в файлах слайсов, рядом с логикой редьюсера. Отдельные селекторы могут находиться и в компонентах (специфичные селекторы, которые не переиспользуются в других местах). Селекторы также могут находиться внутри thunks, так как там есть доступ к полному состоянию.

Селекторы применяются вместе с хуком useSelector.

Проблемы

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

Хук useSelector сравнивает результат выполнения селектора с предыдущим (используя строгое равенство `===`). Если значения отличаются, выполняется перерендер. То есть селектор не должен возвращать новое значение, если ничего не изменилось. Но тут появляется проблема, например, с методами массивов (map, filter), которые всегда возвращают новый массив. Даже если список отфильтрованных элементов не изменился, хук увидит новый массив и посчитает, что нужен перерендер.

Чтобы избежать этого, нужна мемоизация селекторов. (Мемоизация не нужна, если селектор возвращает примитивное значение).

Мемоизация

Для мемоизации мы используем библиотеку Reselect (есть и альтернативы, которые указаны в статье). Она предоставляет функцию createSelector, которая принимает любое количество входных селекторов (для проверки изменения различных частей состояния) и один выходной (собственно для выборки данных). Выходной селектор вызывается только в том случае, если входные селекторы возвращают изменившиеся результаты.

#управлениесостоянием #документация #redux #оптимизация
👍3
Реализация функциональности Шаг назад

Статья в документации: https://redux.js.org/usage/implementing-undo-history

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

Глобально паттерн выглядит так:


{
past: [T, T],
present: T,
future: [T]
}


Текущее состояние хранится в поле present. При каждом изменении старое состояние добавляется в массив past, а present изменяется. Если потребуется сделать Шаг назад, мы просто возьмем последнее состояние из past и установим его в present. При этом текущее состояние нужно сохранить в future, чтобы к нему можно было вернуться.

В статье дана полная реализация алгоритма. Также можно воспользоваться готовой реализацией из пакета redux-undo.

#управлениесостоянием #redux #документация #паттерны
👍3
Я все время неправильно создавал формы в React

Статья (англ.): https://dev.to/kuvambhardwaj/i-was-creating-forms-the-wrong-way-all-along-in-reactjs-hl3

Автор делится своим подходом к созданию форм в React. Вместо многочисленных useState и onChange для каждого поля ввода он использует нативные возможности HTML: тег form, атрибуты name и структуру FormData.

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

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

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

#формы #ссылки #паттерны
👍3
Управление состоянием в React: продолжение

В рамках темы управления состоянием мы разобрались со связкой useContext + useReducer, познакомились с Redux и даже с Redux Toolkit.

В документации Redux еще осталось несколько интересных разделов (Faq, Style Guide, Api Reference), но информация в них во многом повторяется, поэтому пока можно пойти дальше, а к ним вернуться на досуге.

Кроме того, хотелось бы отдельно вернуться к RTK Query - эта тема зашла хуже всего, слишком много абстракций.

Но это все позже, а далее по плану - взглянуть на альтернативы Redux.

#управлениесостоянием #отложено
👍1
Recoil. Первый взгляд

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

Атомы

Состояние представлено в виде атомов. То есть мы не создаем единый объект для всего состояния приложения, как в Redux. А делим состояние на отдельные порции и каждую оборачиваем в функцию atom. Каждый атом при этом должен иметь уникальное имя.

Список элементов TODO-листа будет выглядить вот так:


const todoListState = atom({
key: "todoListState",
default: []
});


А это значение фильтра по статусу элементов:


const todoListFilterState = atom({
key: "todoListFilterState",
default: 'all'
});


Селекторы

Как и в Redux, в атомах рекомендуется хранить минимально возможный объем данных без повторений. Все производное состояние (которое можно вычислить) должно вычисляться. Для этого в Recoil есть селекторы. По сути это просто чистые функции, которые подписываются на изменение атомов (или других селекторов) и пересчитывают при необходимости свое значение. У селекторов тоже должно быть уникальное имя.

Вот так выглядит вычисление отфильтрованного списка элементов:


const filteredTodoListState = selector({
key: "filteredTodoListState",
get: function(get) {
const filter = get(todoListFilterState);
const todos = get(todoListState);

switch (filter) {
case 'completed':
return todos.filter(function (todo) { return todo.isComplete });
case ACTIVE:
return todos.filter(function (todo) { return !todo.isComplete });
default:
return todos;
}
}
})


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

Получение значений

Для получения значений атомов и селекторов есть хук useRecoilValue.

Получаем отфильтрованный список, чтобы вывести его в компоненте.


const todos = useRecoilValue(filteredTodoListState);


Изменение значений

Значение селектора нельзя изменить, так как оно вычисляемое. А для работы с атомами есть хук useRecoilState, который работает точно так же, как useState.


const [filter, setFilter] = useRecoilState(todoListFilterState);


Кроме того есть хук useSetRecoilState. Это по сути второй компонент хука useRecoilState, возвращает только функцию для изменения состояния (атома).


const setTodos = useSetRecoilState(todoListState);


Корневой элемент

Чтобы это все работало, приложение нужно обернуть в компонент RecoilRoot.


import { RecoilRoot } from "recoil";


Демо

Маленькое приложение (TODO-лист), демонстрирующее основы Recoil: https://codesandbox.io/s/recoil-react-junior-3orhdl?file=/src/App.js


#управлениесостоянием #recoil #примерыкода
👍3