Делайте return, как только нашли ответ

Работая с React-проектами, я столкнулся с повторяющейся проблемой: render-функции, которые сложно понять. Я хотел бы рассказать о наиболее частой причине данного усложнения — вложенные if-else операторы в render-функциях, и как этого можно избежать.

Что такое render-функция?

Оттолкнемся от React-документации. Render-функция должна возвращать React Element или нулевое значение. В идеале она должна быть чистой и использовать только this.props и this.state.

Зачем нужен условный оператор в render-функции?

В чистой render-функции условный оператор обычно основан на состоянии/параметрах компонента. Есть две основные причины if-проверок:

  1. Есть ли у нас все необходимые данные?
    if (this.props.user) {
     // Есть данные, отображаем компонент юзера
    } else {
     // Нет данных юзера
    }
  2. Различные варианты отображения на основании state/props.
    if (this.props.showWarning) {
     // Дополнительно нужно отобразить компонент с предупреждением
    }

Но нельзя сказать, что наличие условных операторов в render-функции само по себе плохо или хорошо. Проблема заключается именно в организации таких проверок.

Плохая организация

Давайте перейдем к примеру:

render() {
 let content;
 
 if (this.props.users) {
   if (this.props.users.length) {
     content = (
       <List>
         {this.props.users
            .map(user => <UserCard key={user.id} user={user} />)}
       </List>
     );
   } else {
     content = <Warning>Empty</Warning>;
   }
 } else {
   content = <Loading />;
 }
 
 return content;
}

Выглядит сложновато. Попробуем упростить:

render() {
 if (!this.props.users) {
   return <Loading />;
 }
 
 if (!this.props.users.length) {
   return <Warning>Empty</Warning>;
 }
 
 return (
   <List>
     {this.props.users
        .map(user => <UserCard key={user.id} user={user} />)}
   </List>
 );
}

Какие преимущества мы получаем?

Читабельность

Основная задача нашей функции — это отображение списка пользователей. И после простого рефакторинга мы с легкостью можем сосредоточиться именно на этой части. Почему? Потому что теперь в ней нет никаких вложений и нам не нужно задумываться: «Все ли данные представлены?». Мы сделали все необходимые проверки в верхней части, поэтому ошибок быть не должно. Это снижает когнитивную нагрузку и позволяет сосредоточиться на оптимистичной части кода (happy path). Мы также можем сказать, что логика визуализации пользователей является изолированной и визуально выделяется, и то же можно сказать о каждой защитной проверке.

Меньше ошибок и погрешностей

Нам не нужно работать с глубокой вложенностью, что уменьшает вероятность ошибки со скобками. Если код становится легкочитаемым и понятным, найти в нем ошибку гораздо проще.

Недостатки?

Нет никаких недостатков! Но не все со мной согласятся. Часто люди видят проблему в том, что количество операций возврата растет, а у функции должна быть одна точка выхода. Но если копнуть, кто и почему это придумал, то в основном все дороги ведут к правилу Single Entry — Single Exit.

Single Entry — Single Exit

Здесь проблема заключается в том, что принцип Single Entry — Single Exit используется для предотвращения входа в функцию оператором GO-TO. Суть отдельного выхода состоит в том, что функция возвращается в единое место, а не прыгает при помощи GO-TO в какую-то другую часть приложения, никогда не достигнув возвратного значения. Кроме того, нигде не сказано, как много возвратных значений у вас может быть. Принцип Single Entry — Single Exit сформулировал Эдсгер В. Дейкстра, который решительно выступает против практики использования операторов GO-TO.

Что еще?

В книге Code complete написано следующее:

«Минимизируйте число возвратов из каждого метода. Тяжело понять логику метода, если при чтении его последних строк вы не подозреваете о возможности выхода из него где-то вверху. По этой причине используйте операторы возврата благоразумно — только если они улучшают читабельность».

Но, кроме этого, в книге также сказано:

«Используйте return, если это повышает читабельность. В некоторых методах при получении ответа хочется сразу вернуть управление вызывающей стороне. Если метод определен так, что обнаружение ошибки не требует никакой дополнительной очистки ресурсов, то отсутствие немедленного возврата означает необходимость писать лишний код. Вот хороший пример ситуации, когда возврат из нескольких частей метода имеет смысл».

Как раз в нашем случае множественный возврат уместен из-за улучшения читабельности.

Также в книге есть отдельная глава о рефакторинге и перечень его видов/причин, один из которых я хотел бы подчеркнуть.

«Возврат из метода сразу после получения ответа вместо установки возвращаемого значения внутри вложенных операторов ifthenelse»

Выход из метода сразу же после нахождения возвращаемого значения часто помогает облегчить чтение кода и снизить вероятность ошибок. Альтернативный вариант — установка возвращаемого значения и следование к выходу из метода через заторы операторов — может оказаться более сложным".


Есть еще два похожих паттерна, которые мне удалось найти, — Guard Clause и Bouncer Pattern. Они выглядят примерно так же, как описанный случай.

Оставляйте свое мнение в комментариях!

Похожие статьи:
Тайм-менеджмент, или управление временем, — всё более востребованный навык в современном мире, но для многих людей это понятие всё ещё...
«Каждый, у кого нет машины, мечтает её купить, и каждый, у кого есть машина, мечтает её продать. И не делает этого только потому, что,...
В выпуске не будет скучных ссылок и затертых новостей, неинтересных штук и банальных тулзовин. Все что тут — это интересно...
В выпуске: SwiftUI vs. Auto Layout, ViewModifier, боковое меню и асинхронная загрузка изображений, MapView app, набор расширений. Хочу узнать...
The extreme startup hackathon series Garage48 is coming to Lviv for the second time with a bunch of top notch startup founders and mentors to help you in turning your ideas into working prototype in 48 hours!...
Яндекс.Метрика