Коллеги со стороны бакэнда иногда любезно спрашивают: "а нафига вам тут реакт"? Будем честны и ответим, что без него можно написать довольно приличный код, отдать его на ревью коллеге-фулстеку и получить аппрув после 15 секунд (так быстро не потому, что коллега не беспокоится за качество кода, а просто код весьма компактен, cмотрите ниже). Если подумать чуть-чуть дольше (например, за время заварки чая, которое, как все знают, равно трем минутам), можно найти не менее шести ошибок, а может и больше. Любая ошибка, конечно, весьма субъективна, но я постараюсь объективно объяснить каждую из них.
Ниже код представляет собой обычный список с кнопкой "добавить", код рабочий в том смысле, что его можно скопировать в файл и открыть в браузере.
<body>
<script>
class Component {
constructor(
container,
callback, // callback on add
emails = [], // state
) {
this.container = container;
this.callback = callback;
this.emails = emails;
}
templateForOneEmail(title, body) {
return `
<h4>${title}</h4>
<div>${body}</div>
<hr />
`;
}
template() {
const str = this.emails
.map(([title, body]) => this.templateForOneEmail(title, body))
.join("");
return `
<div id="my-emails">${str}</div>
<button id="my-button">Add</button>
`;
}
subscribe() {
const node = this.container.querySelector("#my-button");
if (node)
node.addEventListener("click", this.callback);
}
unsubscribe() {
const node = this.container.querySelector("#my-button");
if (node)
node.removeEventListener("click", this.callback);
}
render() {
this.unsubscribe();
this.container.innerHTML = this.template();
this.subscribe();
}
clear() {
this.unsubscribe();
this.container.innerHTML = "";
}
}
function main() {
const container = document.createElement("div");
document.body.appendChild(container);
const emails = [];
let comp = null;
const callback = () => {
if (!comp) { return; }
emails.push(["title", "body"]);
comp.clear();
comp = new Component(container, callback, emails);
comp.render();
}
comp = new Component(container, callback, emails);
comp.render();
}
main();
</script>
</body>
Итак, ошибки:
Начнем, пожалуй, с самой безобидной - Array.join в методе template. Вот, не свойство компонента конвертировать между массивом и строкой для рендинга. [jsx]
Перерисовка всего экрана при добавлении элемента. Интуитивно хочется дорисовать, а не перерисовать :) [virtual dom]
Вынужденные неточности с подписками - вызов unsubscribe (ресурсный метод) то тут, то там. Можно возразить, что в данном примере unsubscribe в рендере не нужен, так как мы можем вызвать clear следом, но в этом случае мы, к сожалению, теряем idempotence рендера (см. плиз https://en.wikipedia.org/wiki/Idempotence) [lifecycle]
Ссылка на самих себя в колбеке. Тут хороший вопрос, насколько это плохо для движка V8, но интуитивно создание цикла из ссылок не очень хорошая идея. [тут тег, в котором реакт не одобряет такое]
Создание компонента на каждый колбэк. Тут, возможно, и нет ничего плохого, но данное действие, скорее, увеличивает энтропию (о понимании программы) в целом, чем уменьшает ее. [state observers]
Ручная чистка ресурсов (метод clear нужно вызывать извне), как следствие, легко что-то забыть или потерять. [ownership]
И напоследок, вопрос о композиции таких компонентов - но это, как и на самом среднем проекте, обычно думается потом.
Комментарии (27)
dopusteam
08.04.2023 15:44+4Вот, не свойство компонента конвертировать между массивом и строкой для рендинга
Что это значит? Несколько раз перечитал, но так и не понял
xyli0o Автор
08.04.2023 15:44[на интуиции] Массив каким-либо образом нужно конвертнуть в строку для отрисовки, так? И вот именно это знание не нужно держать в компоненте. Интереснее, как мне кажется, это знание отдать движку (jsx).
Zimtir
08.04.2023 15:44Речь про SRP (single responsibility principle)
Да и DI сделать сложнее
Zimtir
08.04.2023 15:44А вообще пишите функциональный код, если ваш проект пока не определился со стеком технологий, так хотя бы модули на ФП вы сможете потом использовать, а если они еще и юнит-тестами обложены, цены вам нет
Но если бы вы стали городить тут абстракции а-ля адаптеры и так далее — тогда вы делаете велосипед.
https://anywhere.epam.com/en/blog/javascript-functional-programming-vs-oop
dopusteam
08.04.2023 15:44Я даже с точки зрения русского языка не смог распарсить предложение, так что можно, конечно, гадать, но мне так до сих пор непонятно. И непонятно, почему автор в тексте не поправит это
yakimchuk-ry
08.04.2023 15:44+1Есть целое море причин почему нужно использовать библиотеки и фреймворки на коммерческих проектах: скорость найма кадров, порог входа в проект, обучение и обмен опытом, скорость разработки, качество решений, наличие документации, наличие огромного количество
собранных граблейрешенных вопросов на SO, и прочее, и прочее.Как сказали выше, если проект из серии написать и больше не трогать, то можно сделать как угодно, да.
nightlord189
08.04.2023 15:44Если уж отходить от реакта, то можно отойти и от логики "перерендер на каждое изменение", выкинуть все render/subscribe/unsubscribe и добавлять в html новую строчку прямо в обработчике кнопки. Это и куда производительнее будет.
nin-jin
08.04.2023 15:44+1Этот подход очень плохо масштабируется, так как требует, чтобы каждая кнопка знала про рендеринг всего приложения.
nightlord189
08.04.2023 15:44Согласен, так что если приложение большое, то лучше сразу Реакт и взять.
Zimtir
08.04.2023 15:44Первый вопрос, который проект себе должен задать — зачем это мне?
React — реагировать, если вы делаете простой layout без масштабирования и конечных автоматов — тогда вам и js наверное не нужен и при правильной подаче используйте конструкторы типа WordPress, бизнес, что не понимает зачем ему конкретный стек ориентируется на выживание, а не качество кода.
Если бизнес выжил — он уже начнёт пилить 2.0, а тут фактически и не масштабируемое решение, чтобы проверить гипотезу на рынке и не стабильный стек для частых релизов
amakhrov
08.04.2023 15:44не свойство компонента конвертировать между массивом и строкой для рендинга
Нет принципиального отличия от "конвертировать объект ({title, body}) в строку для рендеринга". И то, и другое - сериализация данных в строку.
вызов unsubscribe (ресурсный метод) то тут, то там
По-моему, тут ансабскрайб вообще не нужен нигде. Его GC соберет - на него нигде не осталось ссылок. В древние века в каком-то браузере это бы привело к утечкам памяти - но не в 2023.
Ссылка на самих себя в колбеке
Не вижу ничего страшного.
Создание компонента на каждый колбэк
Необязательно же так делать. Можно сделать
comp.setData(emails)
. И компонент просто перерисуетсяamakhrov
08.04.2023 15:44Основные проблемы с этим кодом - отсутствие экранирования (привет, XSS) и полная перерисовка страницы на каждый чих. Первую проблему можно решить легким шаблонизатором. Вторую - только точечной подпиской на изменения и скурпулезной работой с DOM. И вот тут-то мы получим, в том числе, и проблему с композицией. А если мы перерисовываем все всегда, то компоновать копмоненты - тривиально.
Zimtir
08.04.2023 15:44Тут любой ревьювер должен понять что это программирование ради программирования, кейс с xss — это еще цветочки
Мы пишем код не ради написания кода, а ревьюверы не чтобы тешить свое чсв
Если вы пилите качественный Энтерпрайз, то ревьювер обязан reject-ить такой PR/MR на этапе его создания
Если вы пишите гипотезу абы-как тогда откуда вас тут появилась культура ревью? Форс-пушьте в мастер. Если это какие-то инженерные вилы (не люблю реакт = не понимаю его), тогда бизнес просто тратит деньги на то, чтобы разработчики делали вид, что они не самозванцы, раз так глубоко шарят в надежде написать свой велосипед
Отсюда же вытекает следующее:
Энтерпрайз с большим прошлым и будущим — пишем норм на стабильном решении
Тестим гипотезу — берите no code или ucoz
Нет фронтендеров? Привет ASP.NET MVC
А если они есть, то мне кажется, что зря вы их наняли если их больше 1 человека, потому что они пишут велосипед, пока ваш бизнес пытается жить
Культура написания качественного кода != культура написания своих велосипедов
В разработке нужно отталкиваться от принципов, никто не любит приходить на проекте и разгребать легаси за тем, кого поторопили и кто не разобрался и не смог сказать бизнесу «нет», единственная книга Мартина для рекомендации «идеальный программист»
dom1n1k
08.04.2023 15:44+4Вообще, идея хорошая — показать на практическом примере, какие неочевидные проблемы решает популярный фреймворк (помимо общих рассуждений про удобство и упрощение найма).
Примерно года 3-4 назад был пик холивара вокруг jQuery. Помню, тогда многие писали, что его давно пора закапывать, потому что ванила умеет всё то же самое. Что, конечно, не совсем правда — приводимые в интернетах "эквиваленты" часто были не совсем эквивалентами, упускали существенные детали и тд.
Вот и здесь было бы интересно почитать. Типа есть такая типичная задача, в лоб она решается вот таким подходом, но в этом коде спрятаны с такие-то проблемы. А авторы фреймворка их предусмотрели так-то и так-то.
Но реализация (статьи) неудачная: ничего толком не понятно — ни постановка задачи, ни логика решения, ни объяснения проблем.
Overdozed
08.04.2023 15:44Вы надоели уже экспериментировать. Наэксперементируются и ливают проект. А потом рефактори за ними их эксперименты.
PavelZubkov
08.04.2023 15:44Минутка продаж
Если жалко брать большой реакт для маленького приложения, возьмите $mol
https://pavelzubkov.github.io/my_app
декларативное описание приложения https://github.com/PavelZubkov/my_app/blob/master/app.view.tree
логика к нему на ts
https://github.com/PavelZubkov/my_app/blob/master/app.view.ts
стили
https://github.com/PavelZubkov/my_app/blob/master/app.view.css.ts
Luchnik22
Если это вся суть проекта, то да, но если на этом не останавливаться, то лучше использовать хорошо задокументированную, протестированную, абстракцию с которой большинство разработчиков знакомы (привет React), чем писать
велосипедшаблонизатор без тестов, без документации, с которым придётся разбираться новым разработчикамnin-jin
.. получится..
А если вы к нему добавите ещё и..
..то погоды это не сделает. Более того, это ухудшит архитектуру проекта.