Прежде чем я начну свой рассказ, мне хотелось бы отметить, что вы сможете разобраться в этой статье, даже если никогда не пользовались 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 + 1AsyncStorage.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 + 1AsyncStorage.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-проектах?