HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p><strong>В этой статье мы разберемся, почему использование status enum - или конечного автомата - поможет вашему приложению избежать ошибок, с которыми вы можете столкнуться, используя логические значения.</strong></p>
1 <p><strong>В этой статье мы разберемся, почему использование status enum - или конечного автомата - поможет вашему приложению избежать ошибок, с которыми вы можете столкнуться, используя логические значения.</strong></p>
2 <p><em>Это адаптированный перевод статьи<a>Stop using isLoading boolean</a>Кента Додса, JS-разработчика и преподавателя программирования. Повествование ведётся от лица автора оригинала.</em></p>
2 <p><em>Это адаптированный перевод статьи<a>Stop using isLoading boolean</a>Кента Додса, JS-разработчика и преподавателя программирования. Повествование ведётся от лица автора оригинала.</em></p>
3 <h2>Содержание</h2>
3 <h2>Содержание</h2>
4 <ul><li><a>Про isLoading</a></li>
4 <ul><li><a>Про isLoading</a></li>
5 <li><a>Конечный автомат</a></li>
5 <li><a>Конечный автомат</a></li>
6 <li><a>Вывод</a></li>
6 <li><a>Вывод</a></li>
7 </ul><h2>Про isLoading</h2>
7 </ul><h2>Про isLoading</h2>
8 <p>isLoading (и подобные ему выражения: isRejected, isIdle, isResolved и другие) создают больше проблем, чем решают. Продемонстрирую это на примере эксперимента с<a>API геолокации</a>. Весь код ниже написан на React, но его можно адаптировать к любому фреймворку или языку.</p>
8 <p>isLoading (и подобные ему выражения: isRejected, isIdle, isResolved и другие) создают больше проблем, чем решают. Продемонстрирую это на примере эксперимента с<a>API геолокации</a>. Весь код ниже написан на React, но его можно адаптировать к любому фреймворку или языку.</p>
9 <p>Это классический пример использования логических значений в API геолокации - его используют многие разработчики приложений, которые пишут на JS и других языках. У этого кода есть проблема:</p>
9 <p>Это классический пример использования логических значений в API геолокации - его используют многие разработчики приложений, которые пишут на JS и других языках. У этого кода есть проблема:</p>
10 <p>Если вы видите проблему - отлично. Если нет, разберем еще один пример. Представьте, что пользователь садится в машину и едет по городу. При этом его геолокация меняется, но приложение не успевает ее отследить - например, из-за отсутствия интернета, или невозможности установки текущего положения. Если код построен по тому же принципу, что фрагмент выше, пользователь не увидит ошибки, а приложение будет показывать ему неактуальную геолокацию.</p>
10 <p>Если вы видите проблему - отлично. Если нет, разберем еще один пример. Представьте, что пользователь садится в машину и едет по городу. При этом его геолокация меняется, но приложение не успевает ее отследить - например, из-за отсутствия интернета, или невозможности установки текущего положения. Если код построен по тому же принципу, что фрагмент выше, пользователь не увидит ошибки, а приложение будет показывать ему неактуальную геолокацию.</p>
11 <p>Если показывать геопозицию пользователя только в случае, когда она определена, возникнет противоположная проблема - пользователь видит только сообщения об ошибке, даже если следующие запросы по определению геолокации выполняются успешно.</p>
11 <p>Если показывать геопозицию пользователя только в случае, когда она определена, возникнет противоположная проблема - пользователь видит только сообщения об ошибке, даже если следующие запросы по определению геолокации выполняются успешно.</p>
12 <p><em>Есть несколько решений этой проблемы:</em></p>
12 <p><em>Есть несколько решений этой проблемы:</em></p>
13 <ol><li>Убедиться, что приложение всегда показывает местоположение и ошибку.</li>
13 <ol><li>Убедиться, что приложение всегда показывает местоположение и ошибку.</li>
14 <li>Очистить поле error, если данные о геопозиция получены успешно, или очистить поле position, когда произошла ошибка.</li>
14 <li>Очистить поле error, если данные о геопозиция получены успешно, или очистить поле position, когда произошла ошибка.</li>
15 <li>Вернуть дополнительное свойство, которое определяет текущий статус информации о геопозиции.</li>
15 <li>Вернуть дополнительное свойство, которое определяет текущий статус информации о геопозиции.</li>
16 </ol><p>Большинство приложений разрабатывается таким образом, чтобы пользователь не смог сделать что-то неправильно, даже если ему захочется. Или, по крайней мере, так, чтобы ему было удобнее поступать правильно. Поэтому первый вариант решение проблемы можно сразу исключить.</p>
16 </ol><p>Большинство приложений разрабатывается таким образом, чтобы пользователь не смог сделать что-то неправильно, даже если ему захочется. Или, по крайней мере, так, чтобы ему было удобнее поступать правильно. Поэтому первый вариант решение проблемы можно сразу исключить.</p>
17 <p>Второй вариант решения тоже не идеален: устройства некоторых пользователей могут передавать последние данные о геолокации, даже если произошла ошибка.</p>
17 <p>Второй вариант решения тоже не идеален: устройства некоторых пользователей могут передавать последние данные о геолокации, даже если произошла ошибка.</p>
18 <p>Остается третий вариант. Попробуем реализовать его:</p>
18 <p>Остается третий вариант. Попробуем реализовать его:</p>
19 <p>В этом фрагменте появилась еще одна dispatch, которая поможет нам отличить idle и pending. В данном случае разницы между ними нет, но, в некоторых других ситуациях, нужно разграничивать эти два конечных состояния. Это важный нюанс, на который стоит обратить внимание.</p>
19 <p>В этом фрагменте появилась еще одна dispatch, которая поможет нам отличить idle и pending. В данном случае разницы между ними нет, но, в некоторых других ситуациях, нужно разграничивать эти два конечных состояния. Это важный нюанс, на который стоит обратить внимание.</p>
20 <p>Теперь вместо логических значений мы используем переменную status:</p>
20 <p>Теперь вместо логических значений мы используем переменную status:</p>
21 <p>Использование переменной status вместо isLoading помогает точно узнать, в каком состоянии находится процесс определения геолокации в любой момент времени.</p>
21 <p>Использование переменной status вместо isLoading помогает точно узнать, в каком состоянии находится процесс определения геолокации в любой момент времени.</p>
22 <p>Если вы хотите избавиться от выражений вида variable === 'string' в if, сделайте следующее:</p>
22 <p>Если вы хотите избавиться от выражений вида variable === 'string' в if, сделайте следующее:</p>
23 <p>Здесь возникает пространство для дискуссий - переменные можно хранить в<a>состоянии редьюсера</a>, а не выводить их значения. Однако такой подход делает код уязвимым для<a>невыполнимых состояний</a>.</p>
23 <p>Здесь возникает пространство для дискуссий - переменные можно хранить в<a>состоянии редьюсера</a>, а не выводить их значения. Однако такой подход делает код уязвимым для<a>невыполнимых состояний</a>.</p>
24 <p>Если вы действительно хотите, чтобы вашим пользователям не приходилось использовать variable === 'string', убедитесь, что вы придерживаетесь status в своем состоянии. Это поможет гарантировать, что существует только одно возможное значение конечного состояния. После этого вы можете выводить логические состояния:</p>
24 <p>Если вы действительно хотите, чтобы вашим пользователям не приходилось использовать variable === 'string', убедитесь, что вы придерживаетесь status в своем состоянии. Это поможет гарантировать, что существует только одно возможное значение конечного состояния. После этого вы можете выводить логические состояния:</p>
25 <h2>Конечный автомат</h2>
25 <h2>Конечный автомат</h2>
26 <p><a>XState</a>- это удобная библиотека для реализации<a>конечных автоматов</a>в коде. Посмотрим, как она работает на реальном примере:</p>
26 <p><a>XState</a>- это удобная библиотека для реализации<a>конечных автоматов</a>в коде. Посмотрим, как она работает на реальном примере:</p>
27 <p>Если вы не знакомы с<a>конечным автоматом</a>или с библиотекой XState, возможно, код в этом разделе покажется вам сложным. Любая абстракция становится понятнее со временем, если уделять ей достаточно внимания.</p>
27 <p>Если вы не знакомы с<a>конечным автоматом</a>или с библиотекой XState, возможно, код в этом разделе покажется вам сложным. Любая абстракция становится понятнее со временем, если уделять ей достаточно внимания.</p>
28 <blockquote><h3>Читайте также:</h3>
28 <blockquote><h3>Читайте также:</h3>
29 <p>Как спроектировать<a>правильный конечный автомат</a>на REST</p>
29 <p>Как спроектировать<a>правильный конечный автомат</a>на REST</p>
30 </blockquote><p>В примере выше можно отметить несколько моментов. Во-первых, в нем есть специальное состояние для случаев, когда определение геолокации не поддерживается - это состояние называется терминальным. Оно подходит и для нашего примера - если определение геолокации не поддерживается, невозможно перейти в какое-либо другое состояние. Конечный автомат в текущей реализации гарантирует, что такого никогда не произойдет.</p>
30 </blockquote><p>В примере выше можно отметить несколько моментов. Во-первых, в нем есть специальное состояние для случаев, когда определение геолокации не поддерживается - это состояние называется терминальным. Оно подходит и для нашего примера - если определение геолокации не поддерживается, невозможно перейти в какое-либо другое состояние. Конечный автомат в текущей реализации гарантирует, что такого никогда не произойдет.</p>
31 <p>Во-вторых, в коде больше нет переменной status, которую нужно поддерживать - теперь это лишь часть конечного значения состояния конечного автомата. Другими словами, теперь status встроен в конечный автомат.</p>
31 <p>Во-вторых, в коде больше нет переменной status, которую нужно поддерживать - теперь это лишь часть конечного значения состояния конечного автомата. Другими словами, теперь status встроен в конечный автомат.</p>
32 <p>Вот как мы будем это использовать:</p>
32 <p>Вот как мы будем это использовать:</p>
33 <p>Если вам понравился API выше, вы можете сохранить себе и этот:</p>
33 <p>Если вам понравился API выше, вы можете сохранить себе и этот:</p>
34 <p>Если вам сложно работать с конечным автоматом, то можно найти пример редьюсера, который будет проще и понятнее. Но если вы знакомы с этой абстракцией, изучите<a>реализацию Дэвида Хуршида</a>, автора XState. Он полностью отказывается от useEffect - вместо этого он интегрирует логику подписки в конечный автомат. Это означает, что конечный автомат не зависит от того, какой фреймворк используется в пользовательском интерфейсе.</p>
34 <p>Если вам сложно работать с конечным автоматом, то можно найти пример редьюсера, который будет проще и понятнее. Но если вы знакомы с этой абстракцией, изучите<a>реализацию Дэвида Хуршида</a>, автора XState. Он полностью отказывается от useEffect - вместо этого он интегрирует логику подписки в конечный автомат. Это означает, что конечный автомат не зависит от того, какой фреймворк используется в пользовательском интерфейсе.</p>
35 <h2>Вывод</h2>
35 <h2>Вывод</h2>
36 <p>Этот текст - не столько о пользе status enum и конечных автоматов, сколько о вреде логических значений. Потому что они не могут представлять все фактические состояния, в которых может находиться ваш код в любой момент времени.</p>
36 <p>Этот текст - не столько о пользе status enum и конечных автоматов, сколько о вреде логических значений. Потому что они не могут представлять все фактические состояния, в которых может находиться ваш код в любой момент времени.</p>