Недавно я, используя React Native, занимался разработкой мобильного приложения для медитации
Atomic Meditation. Эта программа помогает тем, кто ей пользуется, выработать привычку медитировать, ежедневно уделяя этому занятию какое-то время. В ходе работы у меня появились серьёзные причины приступить к изучению TypeScript и начать пользоваться им вместо JavaScript в проектах среднего и крупного размера.
Прежде чем я начну свой рассказ, мне хотелось бы отметить, что вы сможете разобраться в этой статье, даже если никогда не пользовались React Native. Я буду всё подробно объяснять. А если вы делали какие-нибудь проекты на React, то, читая эту статью, можете считать, что React и React Native — это одно и то же.
А теперь расскажу о том, как обычный JavaScript втянул меня в неприятности.
День 1: всё идёт как надо
В React Native есть объект
AsyncStorage
, который представляет собой хранилище данных типа ключ/значение с асинхронным доступом к значениям по ключам. Он даёт разработчику очень простой механизм для организации постоянного хранения данных на мобильном устройстве пользователя.
Например, воспользоваться им можно так:
AsyncStorage.setItem("@key", value)
AsyncStorage
позволяет хранить лишь строковые данные. Поэтому для того чтобы поместить в это хранилище число — это число сначала надо конвертировать в строку.
Ниже показано применение React-хука useState
для объявления переменной sessionCount
и для установки её начального значения в 0. Тут же имеется и функция setSessionCount
, которая позволяет менять состояние sessionCount
:
const [sessionCount, setSessionCount] = useState(0)
Предположим, пользователь завершил сеанс медитации (я, напомню, занимался разработкой приложения для медитации). В
sessionCount
хранится общее количество сеансов медитации, завершённых пользователем (я буду теперь называть этого пользователя «Anxious Andy» — «беспокойный Энди»). Это значит, что нам надо прибавить
1
к значению, хранящемуся в
sessionCount
. Для этого вызывается функция
setSessionCount
, в которой и выполняется прибавление
1
к предыдущему значению
sessionCount
. А потом количество завершённых медитаций нужно сохранить в
AsyncStorage
в виде строки.
Всё это надо сделать в некоей функции, которую я предлагаю назвать saveData
:
// Пользователь завершил сеанс медитации…
const saveData = () => {
setSessionCount(prev => {
const newSessionCount = prev + 1
AsyncStorage.setItem("@my_number", newSessionCount.toString())
return newSessionCount
})
}
Всё идёт хорошо, наш Энди, теперь уже не такой беспокойный, тихо закроет приложение, испытывая прилив хорошего самочувствия.
День 2: затишье перед бурей
Беспокойный Энди получает уведомление, которое напоминает ему о том, что через 5 минут начинается его медитация. Но он не только беспокойный, но ещё и нетерпеливый. Поэтому он тут же идёт к себе в комнату, находит своё рабочее кресло, удобно (но при этом — сохраняя ясное сознание) в него садится и открывает программу.
Теперь, когда программа загружается, данные сессии Энди нужно прочитать из хранилища. В React хук useEffect
позволяет выполнять функцию-коллбэк при монтировании компонента.
В коллбэке мы асинхронно получаем данные из хранилища, а после этого вызываем функцию setSessionCount()
, передавая ей эти данные, то есть — «1»
:
useEffect(() => {
AsyncStorage.getItem("@my_number").then(data => setSessionCount(data))
}, [])
Беспокойный Энди успешно справляется с ещё одной медитацией. Поэтому к
sessionCount
надо добавить 1, что позволит сохранить общее число завершённых сеансов медитации.
Новое значение, как и прежде, мы записываем в хранилище:
// Пользователь завершил сеанс медитации…
const saveData = () => {
setSessionCount(prev => {
const newSessionCount = prev + 1
AsyncStorage.setItem("@my_number", newSessionCount.toString())
return newSessionCount
})
}
К настоящему моменту пользователь завершил 2 сеанса медитации.
День 3: буря
Энди, теперь — уже вовсе не беспокойный, достаёт телефон и открывает приложение для того чтобы в третий раз подряд устроить сеанс медитации (и дела у него идут хорошо).
Он хочет узнать о том, как далеко продвинулся в деле выработки полезной привычки. Поэтому он открывает экран статистики. «О, да тут много всего интересного», — приговаривает он. «Отличная программа!».
Но его любовь к этой программе быстро сходит на нет…
Программа сообщает ему о том, что он провёл 11 сеансов медитации. А он-то медитировал всего два раза!
Неправильная статистика по сеансам медитации
Что пошло не так?
В первый день мы записали в
sessionCount
начальное значение — число
0
.
Пользователь завершил сеанс медитации — поэтому мы добавили к sessionCount
1
. Затем мы преобразовали то, что получилось, в строку — в «1»
, после чего записали это в асинхронное хранилище (вспомните — оно может хранить только строковые данные).
Во второй день мы загружаем данные из хранилища и записываем в sessionCount
загруженное значение. То есть — «1»
(строку, а не число).
Пользователь завершает сеанс медитации и мы прибавляем к sessionCount
1
. А в JavaScript «1» + 1
равняется «11»
, а не 2
.
Мы забыли преобразовать строковые данные, считанные из хранилища, в число.
А хуже всего тут то, что наша программа при этом не выдала никаких сообщений об ошибках. Эта ошибка какое-то время оставалась незамеченной, а позже довела нас до неприятностей. Поиск источников подобных проблем может оказаться не таким уж и простым делом.
JavaScript позволил нам свободно, не сознавая того, что мы делаем, поменять в ходе выполнения программы тип данных, хранящихся в переменной.
Решить эту и другие подобные проблемы можно с помощью TypeScript.
Что такое TypeScript?
Если вы не знакомы с TypeScript, то знайте, что это, в сущности, то же самое, что и JavaScript, но оснащённое некоторыми полезными возможностями. В частности, переменные не могут менять типы. А если это случится — TypeScript выдаст сообщение об ошибке.
Браузеры не могут выполнять TypeScript-код. Поэтому TypeScript-файлы проекта надо транспилировать в JavaScript. На выходе получится несколько JavaScript-файлов (или один большой «бандл» с JS-кодом проекта).
Использование TypeScript в React Native-проектах
Добавить поддержку TypeScript в существующий React Native-проект
очень просто. А именно, надо будет кое-что установить из npm и сделать пару настроек.
Теперь нужно будет лишь переименовать файлы с кодом, например — App.js
в App.tsx
, после чего заработает автоматическая система контроля типов.
После того, как изменено расширение файла, TypeScript разразится гневной тирадой о том, что аргумент типа 'string | null'
нельзя назначить параметру типа 'SetStateAction<number>'
.
TypeScript предупреждает разработчика о том, что с типами данных что-то не так
Это значит, что мне тут, чтобы избавиться от сообщения об ошибке, надо, во-первых, проверить data
на null
, а во-вторых — преобразовать из строки в число (воспользовавшись parseInt()
):
useEffect(() => {
AsyncStorage.getItem("@my_number").then(data => {
if (data) {
setSessionCount(parseInt(data))
}
})
}, [])
Использование TypeScript подталкивает разработчика к написанию более качественного и надёжного кода. Это просто замечательно!
По каким материалам изучать TypeScript?
Я изучал TypeScript по
этому видеокурсу канала Net Ninja. И если бы мне надо было бы что-нибудь изучить, то я в первую очередь поинтересовался бы тем, нет ли на этом канале курса по тому, что мне нужно.
Кроме того, официальная документация по TypeScript очень даже хороша.
Итоги
Теперь, благодаря TypeScript, я могу спать немного спокойнее, зная о том, что переменные в моём проекте не могут совершенно неожиданно менять свои типы. Спасибо за это TypeScript.
Да, не могу не отметить, что JavaScript хорошо подходит для маленьких проектов. Но при работе над средними и большими проектами, а так же — над маленькими проектами, которые вполне могут вырасти, лучше, пожалуй, прибегнуть к TypeScript, даже если для этого придётся потратить время на его изучение. А если вы знаете JavaScript, то и TypeScript освоите без особого труда.
Используете ли вы TypeScript в своих React-проектах?