[JavaScript, ReactJS] Почему иногда React/Redux в текущем состоянии give me creeps

Автор Сообщение
news_bot ®

Стаж: 6 лет 3 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
02-Июл-2021 23:31


React существует достаточно давно, чтобы мажорные изменения в этой библиотеке, не ощущались температурой подогрева кресел разработчиков в холодные зимние вечера (не благодарите за лайфхак). Но Facebook сделали ход конем и в свое время выпустили не мажорную, а минорную версию и тем самым сняли с себя ответственность за нестабильность уже существующих миллионов репозиториев, как вы уже поняли я буду рассказывать про версию 16.8.0, а так как мы почти никогда не используем React без Redux в продакшн репозиторияx, то и про него скажу. И сперва давайте поговорим про React. Почему была упомянута нестабильность после внесения “дополнений” 16.8.0, проблема в том что она произошла в головах разработчиков - легким движением руки Facebook сказал нам, знаете, ООП это конечно же хорошо, но функциональный подход лучше. И тут особо ярые и продвинутые ринулись кидать уже существующий подход Statefull Components и Stateless Components и дописывать новыe functional Components с его хуками useState, useCallback, useEffect etc. и только лишь иногда useContext. Штош, в самих этих 4х функциях я ничего плохого и не вижу, в общем-то:
  • Динамическое именование проперти стейта - Fine
  • не нужно выстраивать структуру стейта и запоминать ее для обновления - Excellent
  • Можно использовать хук для нескольких изолированных экземпляров стейта даже в одном и том же компоненте - Splendid
  • А главное  - это не нужно запоминать все примочки с Lifecycle - тут все сразу понятно - срабатывает сразу после рендера, а если добавишь clean-up возвращаемую функцию то она сработает сразу же перед удалением компонента из дерева, чего уже говорить про то что строк кода нужно писать меньше - Amazing (c) Тим Кук
И вот, читаешь это и глаз радуется и уже как бы и не злой ты на Фейсбук и тут находишь это (прим. с офф сайта):
import React, { useState, useEffect } from 'react';
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });
  return isOnline;
}
А теперь давайте этот пример расширим до жизненных реалий (все хуки в разных файлах):
// useFriednStatus.js
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import * as actions from 'actions';
import { getLoggedInUserSelector } from 'selectors';
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);
  function handleStatusChange(status) {
    setIsOnline(status.isOnline);
  }
  useEffect(() => {
    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });
  return isOnline;
}
// useBestFriendNotifier.js
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import * as actions from 'actions';
import { getLoggedInUserSelector } from 'selectors';
function useBestFriendNotifier(currentUserId) {
    const loggedInUser = useSelector(getLoggedInUserSelector);
    const isBestFriendOnline = useFriendStatus(loggedInUser.bestFriendId);
    const dispatch = useDispatch();
    const notifyMeAboutBestFriendActivity = React.useCallback(() => {
        dispatch(actions.notify(isBestFriendOnline));
    }, [isBestFriendOnline]);
    return notifyAboutBestFriendActivity;
}
// Notification.jsx
import { bestFriendOnlineSelector } from 'selectors';
function Notification(({ id }) => {
    const notifier = useBestFriendNotifier(id);
    const isOnline = useSelector(bestFriendOnlineSelector);
    return (
        <p>Your Best friend is { isOnline ? 'Online' : 'Offline' }</p>
    );
});
Эта вложенность может быть и больше, а код уже нечитаемый, приходиться заниматься рекурсивным чтением чтобы понять что было в самом начале и откуда берётся значение, чтобы, например, найти в каком поле Store храниться текущий пользователь.
Как всегда выходит в данном случае, все что ты даешь разработчику написать самому может стать предметом долгих обсуждений. И можно сказать дискоммуникация, плохой лид, плохой разраб, отсутствие конвенции, но если так подумать: есть новый непроторенный ни лидом, ни другими разработчиками подход, и как бы и в официальной документации НЕ написано жирными буквами:
Please, do not use more then one level nesting of custom hooks 
И вот у тебя уже в проекте Stateful Componetns поверх Stateless Componetns, а некоторые перекочевали в function Components (правильно, с хуками, которые под Stateful) и просто function Components, а все потому что нам заботливо написали в документации.
We don’t recommend rewriting your existing components overnight but you can start using Hooks in the new ones if you’d like.
И часто не все разработчики, в силу опыта, а точнее его отсутствия видят картину целостно, то есть ты себе сидишь, пишешь рекурсивные вложенные хуки, код знаешь, место при надобности найдешь, да вот время идет, проекты меняются, а на твое место приходят новые разработчики, которые твой код видят в первый раз.
Все это осознание приходит позже, и что оно дает, правильно - новый подход, четвертый, более чистый - с одноуровневыми кастомными хуками, да только старый подход никуда не делся и если не следить за новым разработчиком который видит существующий легаси и думает - ну и так сойдет и через раз пописывает те самые рекурсивные кастомные хуки. А практика показывает - у лида, архитекторов, разработчиков и других людей заинтересованных смотреть пул реквесты и обновлять конвенцию, и так слишком много работы чтобы присматривать за уже состоявшимся и само-организованным участником Skrum команды, и подход будет плодиться.
Единственным решением в этой ситуации я вижу возможную в будущем пометку в офф. документации - чтоб не использовали вложенность больше одного уровня, а еще лучше прописать такую рулу в линтер, чтоб особо пытливые руки не дотянулись.
Ну и пару слов хочется сказать про redux-thunk, та же проблема - вложенные dispatch() в dispatch(), несколько dispatch() вызовов в одном action где один dispatch() может вызывать чистый action with type ... payload, а вот другие иметь больше вложенных dispatch() и даже с TypeScript не всегда удается отследить отправляемый пейлоад:
function App(() => {
    dispatch(actions.init());
});
// actions.ts
export const init = (): ThunkAction<void, RootState, void, AppAction>  =>  {
    return async (dispatch, getState) => {
        try {
            const user = await getUser();
            if (!user) {
                dispatch(setNotLoggedInUserState());
            }
            dispatch(setLoggedInUserState(user));
        } catch (e) {
            dispatch(showErrorModal);
        }
    };
}
const setNotLoggedInUserState =  (): ThunkAction<void, RootState, void, AppAction> => {
    return async (dispatch, getState) => {
        dispatch(setDefaults());
        dispatch(showLoginModal());
    };
}
const setLoggedInUserState = (user): ThunkAction<void, RootState, void, AppAction> => {
    return async (dispatch, getState) => {
        dispatch(wellcomeBackModal(user.name));
    };
}
// ...
ЗаключениеЭто всего лишь рассуждения на тему того что не всегда модное === лучшее, и возможно, стоило бы уступить место более понимаемому подходу если все равно профита в перфоманс приложения не наблюдается. Впрочем, сами разработчики Facebook в некоторых статьях признаются, что часто могут отказаться от понимаемого кода во имя минимизации строк, например.
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_javascript, #_reactjs, #_react, #_react.js, #_redux, #_javascript, #_statefull, #_stateless, #_javascript, #_reactjs
Профиль  ЛС 
Показать сообщения:     

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

Текущее время: 10-Май 04:55
Часовой пояс: UTC + 5