Робіть 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 . Вони виглядають приблизно так само, як описаний випадок.

Залишайте свою думку в коментарях!

Опубліковано: 06/06/17 @ 10:00
Розділ Різне

Рекомендуємо:

Українці в екосистемі Facebook
Python digest #14: Python обганяє PHP StackOverflow. Event loop in Rust
Прогнозування часових рядів за допомогою Prophet від Facebook
10 плюсів роботи в ніші ЕСЕЙ баксовыми виплатами
Інфо-сайти: підсумки травня 2017 рекорд по трафіку і кількості опублікованих статей