Главная » Frontend » Выбираем middleware в React.js

Выбираем middleware в React.js

Сегодня повествование пойдет в большей степени для новичков, которые только начинают работать с реактом. Как все мы знаем React — это лишь библиотека, а не фреймворк, а значит всю обвязку, включая state management, разработчику нужно выбирать самому.

Рассмотрим очень распространенный на сегодняшний день стек React/Redux. В проектах на данном стеке чаще всего можно встретить три типа миддваров — thunk, saga и redux-observable. При обучении чаще всего в уроках можно встретить лишь thunk, и это весьма оправдано — асинхронных экшенов достаточно, чтобы написать красивый выразительный код и реализовать без проблем любой функционал. Лишь за редким исключением какие-то встроенные сайд-эффекты в саги могут немного облегчить жизнь, в то же самое время не составит труда всё то же самое имплементировать написав пару дополнительных строк кода для создания вспомогательных оберток, использующих стандартные функции, например, библиотеки lodash.

Итак, рассмотрим главные плюсы и минусы, с которыми вы обязательно встретитесь в работе с данными миддлварами. Сначала рассмотрим саги и rx, а thunk оставим на последок, ведь он всех рвёт по моим оценкам.

Redux Saga

Два основных минуса — наложение следующих ограничений:

1. Все данные, с которыми работают саги — всё должно храниться в глобальном стейте. Всегда и как иначе.

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

Из-за этих ограничений существует куча кейсов, когда саги только мешают — это те случаи, когда лучше и удобнее инкапсулировать состояние внутри компонента, а не выносить в глобальный стор. Пример — редактирование комментария. Где должен храниться статус сохранения комментария? Этот статус никому, кроме самого компонента комментария не нужен, я бы положил его внутри компонента в useState. Однако саги меня заставят вынести статус в глобальный стор. И чтобы понять в компоненте, что закончилось действие сохранения редактирования комментария, мне потребуется в каждом таком компоненте писать useEffect и слушать как изменился статус.

Таким образом, получится примерно такой код:

const getLikeStatusByCommentId = useSelector(getLikeStatus);
const likeStatus = getLikeStatusByCommentId(commentId);
const prevLikeStatus = useRef('');

useEffect(() => {
    if (prevLikeStatus.current !== likeStatus) {
        prevLikeStatus.current = likeStatus;
        if (likeStatus === 'done') {
            console.log('success');
        } else if (likeStatus === 'failed') {
            console.log('failed');
        }
    }
}, [likeStatus]);

const handleClick = () => {
    likeComment();
}

То есть дополнительно потребуется подтягивать данные из глобального стора о статусе запроса и сравнивать на каждый чих изменилось ли оно. Конечно же, можно сделать хук — useChangeStatus, однако сам факт, к тому же это все равно не позволит сделать то, что можно сделать на thunk.

Redux Observable

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

К тому же если вы будете использовать TypeScript, и в каком-то эпике возникнет ошибка несоответствия типов, то VSCode подсветит не конкретный кусочек, а прям весь ваш двухстраничный код эпика, а rx он ещё как многословен в реальных проектах, а не в демках. Средств нормального дебага нет. Поэтому соваться новичкам крайне не рекомендую.

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

Redux Thunk

Сразу скажу если бы я выбирал миддвару для нового проекта, то выбрал бы именно thunk. У него практически нет минусов. Вернее есть один, но только если вы пишите тесты, их будет чуть сложнее писать. Хотя может быть вы пишете только юнит-тесты, e2e-тесты, снапшот и скриншот-тесты, тогда вы, возможно, даже этого не заметите.

В остальном всё шикарно — вообще прям всё, все недостатки, которые были указаны выше, их попросту нет.

Например, на redux thunk код будет примерно следующим. Храним состояние в самом компоненте, обрабочик простой и выразительно описывает всю суть — сделать запрос, дождаться его окончания и сделать что-то после, а ошибку обработать отдельно.

const [likeStatus, setLikeStatus] = useState('initial');

const handleClick = async () => {
    setLikeStatus('pending');
    try {
        await likeComment();
        setLikeStatus('done');
        console.log('success');
    } catch(error) {
        setLikeStatus('failed');
        console.log('failed');
    }
}

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

А в дополнении мы может баблить ошибку компоненту-родителю. Просто делаем в родителе обертку для экшена и прокидываем в наш компонент эту обертку, а catch достаточно бросить ошибку с нужными нам данными. Таким образом, мы может обработать ошибку на любом уровне — такого не достичь ни в сагах, ни в observable.

Понравилась статья? — Ставь лайк!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

×
Новости и обзор новинок рынка строительной техники.
Подпишитесь на обновления нашей группы!