Я видел много споров по поводу объектно-ориентированных принципов (а именно S.O.L.I.D.
), MVC
и MVVM
, основанных на критике хуков React
, но ни одного из самых основных принципов чистого кодирования.
Ваша функция должна делать одну вещь, и это должно быть очевидно.
Отказ от ответственности: я не хочу ругать хуки React
, я использую их, и они довольно эффективны. Я хочу заметить, что вместо того, чтобы писать умный код
, который может делать гипотетически все, что угодно, давайте потратим мозговые силы на написание очевидного кода
.
Принцип наименьшего удивления вы знаете…
- Три совершенно разные вещи можно сделать с помощью useEffect
- Запускать какой-либо побочный эффект при каждом рендере
- Запуск побочного эффекта при изменении зависимости
- Примечание: не ожидайте от React мастерства!
- Запускайте побочный эффект только при первом рендере
- Небольшая история, прежде чем я перейду к решению
- Что они должны были сделать с useEffect.
- Завершение
Три совершенно разные вещи можно сделать с помощью useEffect
Запускать какой-либо побочный эффект при каждом рендере
Я думаю, что этот довольно чистый!
function Stuff() {
useEffect(() => document.title = 'I run on every render!');
return <div>stuff!</div>
}
Запуск побочного эффекта при изменении зависимости
Именно здесь все становится диким для каждого новичка в React! Тонкость useEffect
не всегда может быть запущена!
function Stuff({ randomId }: properties) {
useEffect(() => {
// this should update something as well on the UI
// but I omit many moving parts for brevity
fetchThatThing(randomId);
}, [randomId]);
return <div>stuff!</div>
};
Для тех, кому это знакомо, все просто:
- вы запускаете побочный эффект, когда
randomId
меняется - вы гарантируете, что он запускается только при указании
randomId
в массиве.
Но тогда люди, которые только что прочитали документацию и не прокрутили вниз до соответствующих частей, будут делать одно из этого:
function Stuff1({ randomId }: properties) {
useEffect(() => {
fetchThatThing(randomId);
// running fetchThatThing at every render!
});
return <div>stuff!</div>
};
function Stuff2({ randomId }: properties) {
useEffect(() => {
fetchThatThing(randomId);
// passing the dependency the wrong way!
}, randomId); // or { randomId } for total chaos
return <div>stuff!</div>
};
В этом случае мы можем выразить свое превосходство, так как мы потратили много времени, чтобы прочитать все документы и пройти все тренинги, или просто молча исправить ошибку нашего товарища.
Примечание: не ожидайте от React
мастерства!
Вы можете возразить, что запомнить эти 3 проблемы легко.
Но позвольте мне представить вам другую точку зрения!
Допустим, вы работаете в крупной корпорации с множеством разработчиков, слабым правом собственности на код, т.е. любой может создать pull request в любой репозиторий.
У вас есть команды, работающие с Vue.js
, AngularJS
или чем-то еще. И иногда этим людям нужно внести небольшое изменение в ваше приложение React
.
Это могут быть даже опытные фронтенд-разработчики, привыкшие досконально знать другие фреймворки, но не React
в частности. Или они используют изученный React
, но так как они full-stack, они просто прикасаются к фреймворку раз в 3 месяца.
В реальном мире вы не можете ожидать, что все будут свободно говорить на React
, вы должны ожидать очень разнообразную глубину знаний. Любой дизайн, который ожидает, что разработчики будут досконально знать все тонкости фреймворка, создаст трение: уменьшение количества запросов на исправление, переделки, трата времени на исправление недостатков.
Именно поэтому вы должны сделать все очевидным, чтобы люди не совершали ошибок, которых можно избежать.
Запускайте побочный эффект только при первом рендере
Почти в каждом приложении есть шаги инициализации, и тогда люди будут спрашивать вас, гуру компании React
:
«Эй, мне нужно запустить этот скрипт всего один раз при запуске приложения, но этот чертов useEffect
все время его запускает!».
Вы закатываете глаза и говорите, что им просто нужно передать пустой массив []
в качестве зависимости. «Как очевидно!» сказал никто и никогда.
function Stuff() {
useEffect(() => {
// so now this thing will run once the
// first time it is rendered
fetchThatThing(randomId);
}, []);
return <div>stuff!</div>
};
Небольшая история, прежде чем я перейду к решению
Я начал свою настоящую карьеру разработчика в качестве инженера по автоматизации программного обеспечения. Нам нужно было писать сценарии, используя фреймворк автоматизации пользовательского интерфейса, который нажимал на кнопки и ждал, пока произойдет навигация.
Одна из команд автоматизации начала внедрять инновации и поняла, что на самом деле они очень часто нажимают и ждут, пока кнопка исчезнет. Им пришло в голову, что они могут просто объединить эти два действия в вызове функции:
click(waitForDisappearing: boolean) { ... }
Я опускаю детали, но вот как это выглядело, когда вы это использовали:
// let's click on "Register" button
// and then wait until it is unloaded from the screen
registerButton.click(true);
Ну, что??? Что такое click(true)
? Истинный клик
? Существуют ли ложные, злые, обманчивые click
?
Они нарушили очень важный принцип:
Не передавайте
флаги
испециальные параметры
, которые дико изменяют поведение функции нетривиальными способами! Если вы хотите использовать флаги, создайте отдельные функции.
Так что они могли бы просто сделать следующее:
// this can be read and understood by your manager...
registerButton.clickAndWaitUntilItDisappears();
Просто и очевидно.
Что они должны были сделать с useEffect
.
Именно здесь я говорю: запомнить меньше сложнее, чем запомнить много.
Запомнить меньше функций иногда сложнее, чем больше.
Я не вижу, к каким проблемам это могло бы привести:
function Stuff({ randomId }: props) {
useEffectOnInit(() => {
// no need to pass in `[]`
// runs at first render
});
useEffectPerRender(() => {});
useEffectOnPropChange(
() => {},
// Mandatory array!
// Run time warning if empty!
[randomId]
);
return <div>stuff!</div>
};
Именование немного неуклюжее, но использует терминологию, с которой знаком каждый разработчик. Очевидно, что при наличии времени и альтернатив это можно сделать лучше.
Но суть вы поняли.
Когда разработчики из другой команды придут и скопируют код из вашего проекта, чтобы выполнить требования своего проекта, они без проблем поймут, что все это значит и как с этим взаимодействовать.
Завершение
Помните: чистое кодирование — это не объектно-ориентированная парадигма программирования. Это набор с трудом заработанных советов по удобству программирования, которые каждый разработчик должен знать и использовать в своей работе.
Сократив длину функций, необходимых для изучения, они создали очень крутую кривую обучения для тех, кто только начинает или редко использует React
.
Если вы все еще глубоко не согласны со мной, возьмите бэкенд-разработчика javascript
за руку, усадите его на стул и объясните ему эти 3 случая использования useEffect
. Не забывайте следить за их лицами! Я видел их реакцию много раз 😅. «Frontend — это глупость/слишком безумно, чтобы его изучать!».