[JavaScript, ReactJS] Почему иногда React/Redux в текущем состоянии give me creeps
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
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
===========
Похожие новости:
- [Java, IT-компании] Spring MVC vs Spring WebFlux. Что лучше? Объясняем на пингвинах
- [JavaScript, Maps API, ReactJS] Использование mapbox-gl в React и Next.js
- [] Зачем Тинькофф Путешествиям офлайн-режим
- [Высокая производительность, Разработка веб-сайтов, JavaScript, Программирование, Клиентская оптимизация] Оптимизации в вебе — дорого, сложно, и уже не нужно?
- [JavaScript, Программирование, Конференции, Видеоконференцсвязь] Видеочат с возможностью совместного редактирования текста при помощи Twilio Sync (перевод)
- [JavaScript, Разработка мобильных приложений, Разработка под Tizen, Производство и разработка электроники, Мониторы и ТВ] Разработка Tizen-приложений для Samsung Smart TV: полный гайд для Javascript-разработчиков
- [Java] Реактивное программирование со Spring (перевод)
- [Java] Реактивное программирование со Spring, часть 4 R2DBC (перевод)
- [Java] Реактивное программирование со Spring, часть 3 WebFlux (перевод)
- [Java] Реактивное программирование со Spring, часть 2 Project Reactor (перевод)
Теги для поиска: #_javascript, #_reactjs, #_react, #_react.js, #_redux, #_javascript, #_statefull, #_stateless, #_javascript, #_reactjs
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:47
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
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х функциях я ничего плохого и не вижу, в общем-то:
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> ); }); Как всегда выходит в данном случае, все что ты даешь разработчику написать самому может стать предметом долгих обсуждений. И можно сказать дискоммуникация, плохой лид, плохой разраб, отсутствие конвенции, но если так подумать: есть новый непроторенный ни лидом, ни другими разработчиками подход, и как бы и в официальной документации НЕ написано жирными буквами: Please, do not use more then one level nesting of custom hooks
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)); }; } // ... =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 02:47
Часовой пояс: UTC + 5