Ошибка проектирования чистого кодирования `useEffect`

Я видел много споров по поводу объектно-ориентированных принципов (а именно S.O.L.I.D.), MVC и MVVM, основанных на критике хуков React, но ни одного из самых основных принципов чистого кодирования.

Ваша функция должна делать одну вещь, и это должно быть очевидно.

Отказ от ответственности: я не хочу ругать хуки React, я использую их, и они довольно эффективны. Я хочу заметить, что вместо того, чтобы писать умный код, который может делать гипотетически все, что угодно, давайте потратим мозговые силы на написание очевидного кода.

Принцип наименьшего удивления вы знаете…

Три совершенно разные вещи можно сделать с помощью 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 — это глупость/слишком безумно, чтобы его изучать!».

Оцените статью
devanswers.ru
Добавить комментарий