Русский
Русский
English
Статистика
Реклама

React-native

Перевод Удвойте скорость написания кода на React с помощью этих простых трюков

12.03.2021 20:06:18 | Автор: admin

Если вы работаете с React или React Native и чувствуете, что у вас медленная скорость написания кода, вы тратите много времени на поиск ошибок, а не на добавление нового функционала, работаете с большими исходными файлами и вам сложно находить нужные части кода, а также многократно реализуете одну и ту же логику, то эта статья для вас. Вы удвоите скорость написания кода, если отрефакторите код в повторно используемые строительные блоки.

Мы рассмотрим React компонент, который можно хорошо разобрать. И мы будем шаг за шагом реорганизовывать его до хорошего уровня. Вы увидите большое количество идей, которые повысят вашу скорость написания кода.

С повторно используемыми компонентами и хуками легко создать что-то потрясающее. Это похоже на сборку частей Lego.

Cory House. Проектирование повторно используемых компонентов React

Рефакторинг, рефакторинг и рефакторинг это нормально; вам придётся менять свой код много-много раз; это естественный процесс обучения.

Tomas Eglinskas. Самые важные уроки, которые я извлёк за год работы с React

  • В этой статье будет много кода на React. Не бойтесь кода. Не торопитесь, прочтите и поймите его.

  • Это большая статья. Содержание всеобъемлющее. Не стесняйтесь сохранить эту статью в своих вкладках и прочитать её несколько раз.

  • Даже несмотря на то, что код будет на React Native, вы можете легко использовать эту статью, чтобы улучшить свои навыки React.

Исходный компонент, который мы будем рефакторить

Простой компонент, написанный таким же разработчиком, как вы. Что он делает:

  • Получает список браузеров с сервера.

  • Показывает состояние загрузки на экране.

  • Показывает загруженный список в виде списка карточек.

  • Когда пользователь нажимает на карточку, появляется модальное окно с подробным описанием браузера.

Этот код похож на ваш?

Чем хорош этот компонент:

  • Он корректно работает: список будет загружен и показан пользователю со всеми описанным выше функционалом.

  • Он использует React Hooks.

Что плохого в этом компоненте

Этот компонент является монолитом, потому что он был спроектирован и написан для использования в одном месте в одном приложении только один раз.

Запомните эту мантру:

Искусство быстрого разработчика это искусство писать повторно используемые строительные блоки.

Чем больше возможностей для повторного использования ваших компонентов и функций, тем вы быстрее. Это даёт вам огромное преимущество:

  • Вы тратите меньше времени на написание кода. Повторно используемые компоненты/функции могут скрывать тонны кода внутри себя и могут использоваться сотни раз. А реализовать их нужно будет только один раз. Представьте, что каждый раз, когда вам нужно обратиться к API, вы используете fetch(URL), а это замедляет вашу работу.

  • Вы тратите еще меньше времени на написание кода. Гораздо быстрее добавлять новые функции в одно место, чем добавлять их в 10 разных мест. Если вы повторно используете свои строительные блоки, у вас будет только одно место для добавления ваших функций. Реализуйте функцию в одном месте, а результаты получайте везде.

  • Вы тратите меньше времени на поиск ошибок. Вы будете использовать уже проверенные строительные блоки. Новый код нужно тестировать, но старому можно доверять.

  • Вы тратите меньше времени на чтение кода. Если вы повторно используете строительный блок, это сэкономит ваше время на понимание кода. Легче создавать программы из знакомых частей.

  • Вы тратите еще меньше времени на чтение кода. Отдельные повторно используемые строительные блоки можно использовать в разных файлах, поэтому вы будете работать с меньшими модулями и на вашем экране будет меньше кода. Также это улучшит понимание вашего кода.

Разработчик обычно тратит больше времени на выяснение того, что делает код, а не на его написание. Alexis Mangin. Почему разработчики React должны разбивать свои приложения на модули?

  • Вы пишете меньше тестов для повторно используемых строительных блоков. Тесты делают вашу жизнь сладкой, как мёд, но с монолитом вы обнаружите, что снова и снова проверяете одну и ту же логику. Больше повторно используемых строительных блоков = меньше тестов.

Монолит = медленное написание кода.

Многоразовые строительные блоки = быстрое написание кода.

Как сделать эти 100 строк кода более пригодными для повторного использования

Давайте сначала попробуем что-нибудь действительно базовое и простое, а затем перейдём к более сложным вещам. Как мы можем распределить эти 100 строк в разные места?

Перенести константы в пропсы вашего компонента

Мы переместили константу URL-адреса в пропсы и уже можем повторно использовать компонент Browser с другим URL-адресом. Несомненно, небольшое влияние на повторное использование, но это очень легко реализовать.

Разогрелись? Тогда давай попробуем что-нибудь действительно крутое.

Разделите бизнес-логику и интерфейс

Жизнь проще, когда компоненты пользовательского интерфейса не знают о сети, бизнес-логике или состоянии приложения. Передавая одни и те же пропсы, всегда отображайте одни и те же данные. Eric Elliott, Недостающее введение в React

Это самая важная часть статьи. Если вы можете выучить только одну вещь, пусть ею будет она:

Искусство быть быстрым React-разработчиком это искусство отделения бизнес-логики от интерфейса.

Ух ты, какой всплеск сложности! Не бойтесь, я научу вас.

Бизнес-логика: логика, которая принимает решения и сохраняет состояние, например: всё, что находится в <Browsers /> выше ключевого слова return.

Интерфейс: всё, что отображает состояние на экране и считывает ввод пользователя: всё, что находится внутри return()

Давайте сделаем небольшой шаг вперёд и разделим наш компонент на 2 части, а затем посмотрим, как это сделает наш код более пригодным для повторного использования.

Что мы будем делать:

  • Создадим кастомный хук useBrowsers() для нашей бизнес-логики.

  • Создадим компонент <BrowsersList /> с множеством пропсов для нашего интерфейса.

Хорошо, наш код далёк от совершенства, но стал более пригодным для повторного использования:

  • Мы можем использовать <BrowsersList /> с другим источником данных. Раньше мы были обязаны использовать данные только с одного места, но теперь мы можем получить их из памяти, с диска или любого другого места.

  • Мы можем использовать useBrowsers() с другим экраном или даже без экрана. Например, мы можем использовать useBrowsers() в другом приложении с другим дизайном.

Но наш разделённый код далёк от совершенства! Мы всё ещё можем сделать этот код более пригодным для повторного использования.

Повторно используемые строительные блоки = быстрое написание кода.

Разделите свой код на множество небольших файлов

Перво-наперво. Разделение монолита на 2 части открыло новые возможности для рефакторинга. Мы можем сделать нашу программу более читаемой (а вас быстрее), если разделим наш код на разные файлы/модули. Это позволит нам думать о каждом модуле изолированно,а также сделать каждый модуль более читабельным и многоразовым.

Типичная структура проекта на ReactТипичная структура проекта на React
  • index.js экспортирует <Browsers /> с Browsers.jsx.

  • В папках components и hooks хранятся строительные блоки, относящиеся к компоненту <Browsers />.

  • BrowsersList.jsx также может превратиться в папку со связанными хуками, компонентами и индексом.

Файловая структура проекта React представляет собой рекурсивное дерево.

Возможная рекурсивная структура проектаВозможная рекурсивная структура проекта

Подробнее о структуре React-приложений читайте в статье автора David Gilbertson На 100 % правильный способ структурировать React-приложение (или почему такого способа нет).

Давайте посмотрим на наш BrowsersList.jsx.

Стало гораздо меньше кода. Он стал гораздо менее загромождённым. Теперь мы можем сосредоточиться на рефакторинге этой части кода.

Меньше беспорядка на экране = быстрое написание кода

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

Большая проблема с множеством небольших повторно используемых файлов

Посмотрите на наш компонент <BrowsersList />:

  • если мы переименуем некоторые пропсы, например changeDescription в setSelectedBrowser или description в browser,

  • или мы удалим некоторые пропсы,

  • или добавим некоторые пропсы,

В каждом месте, где используется <BrowsersList />, будет ошибка!

Каждый раз, когда вы меняете входящие данные компонента, вы ломаете каждое место своего кода, в котором используется этот компонент. И вы будете часто менять входные данные, так как сложно всё продумать в первый же день, например: дизайн часто возникает из того, как вы пишете код. А поскольку мы сосредоточены на создании множества компонентов, которые можно использовать повторно, во многих местах будут возникать ошибки.

Ваша IDE не может переименовывать пропсы с помощью автоматического рефакторинга. Вам нужно будет сделать это вручную. Вручную проверить каждое место использования. Это замедлит вашу работу.

И хуже всего то, что мы получим ошибку и не узнаем об этом. Единственный способ поймать её это:

  • запустить приложение;

  • вручную перейти к сломанному компоненту и

  • получить ошибку;

  • прочитать текст ошибки;

  • исправить ошибку и попробовать ещё раз.

Это медленная и утомительная задача. Вот так неприятные баги попадают в продакшн. А такое бывает очень часто.

Проверьте, работает ли наш Browsers.jsx?

Кто знает! Для уверенности нам нужно открыть BrowsersList.jsx и useBrowsers.js и вручную сравнить входные данные.

Этот код не работает?

Да. У descripton в useBrowsers.js отсутствует i, это опечатка.

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

Перестаньте медленно писать код на JavaScript, ускорьте себя с помощью TypeScript!

Наступил 2021 год, и каждый разработчик React/React Native должен использовать TypeScript. Фактически нет причин не использовать его.

Он может выглядеть страшно.

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

Наиболее очевидное решение этой проблемы просто включите TypeScript, не меняя код, добавьте несколько простых объявлений типов. В конце концов, наш опыт показывает, что при простейшем использовании TypeScript можно получить много преимуществ. Felix Rieseberg, TypeScript в Slack.

Классные фичи TypeScript

Меньше ошибок.

Меньше ошибок = более быстрое написание кода.

38% ошибок в Airbnb можно было предотвратить с помощью TypeScript, согласно анализу.

TypeScript избавляет от ошибок.

Мы бы сразу нашли опечатку с буквой i:

TypeScript выделяет ошибкиTypeScript выделяет ошибки

Ваша IDE получит функцию автозаполнения для пропсов в React. Это особенно экономит ваше время, когда вы используете сторонние компоненты с неизвестными пропсами:

TypeScript автоматически предлагает вариантыTypeScript автоматически предлагает варианты

Ваша IDE получит функцию рефакторинга. Это сэкономит вам время на переименование пропсов. Все использования пропса будут автоматически переименованы одним щелчком мыши.

Вы никогда больше не забудете добавить проверку на null/undefined.

? означает, что параметр может быть неопределённым.? означает, что параметр может быть неопределённым.

И, конечно же, мы получим сообщение об ошибке, если попытаемся поместить недопустимые данные в наш пропс:

TypeScript проверяет правильность пропсов.TypeScript проверяет правильность пропсов.

TypeScript легко сэкономит вам массу часов и избавит от стресса, связанного с отладкой кода.

JavaScript = медленное написание кода.

TypeScript = быстрое написание кода.

Первый шаг к быстрому проектированию системы: определите свои типы.

Это неполное руководство по TypeScript. Если вы чувствуете, что застряли, перейдите на typescriptlang.org, а затем вернитесь к статье.

Вернёмся к нашему компоненту <BrowsersList /> и определим некоторые типы для его пропсов (не забудьте переименовать файл с .jsx в .tsx).

И обновите сигнатуру хука useBrowsers():

Теперь TypeScript проверит, что useBrowsers() и BrowsersList совместимы. Если мы когда-нибудь изменим входные данные для BrowsersList, мы получим ошибку. Один этот факт гарантирует гораздо меньше ошибок в продакшне.

Меньше ошибок = более быстрое написание кода.

Быстрая системная архитектура

BrowsersListProps в настоящее время выглядит беспорядочно:

  • Компонент должен показывать состояние загрузки. Используется 1 строка в определении типа.

  • Должен отобразиться список Browser[]. Используется 1 строка в определении типа.

  • Должно появиться модальное окно с подробным описанием браузера. Используются 4 строки в определении типа. Эти строки ограничивают нас отображением поля description в нашем модальном окне. И кажется, что есть вероятность, что нам понадобится отобразить больше полей браузера в этом модальном окне. Помните, очень дорого менять сигнатуру компонента.

Мы можем сделать предположение и сократить определение типа и реорганизовать функцию модального окна, чтобы использовать тип браузера.

Это снизит сложность пропсов:

TypeScript проверяет правильность сигнатуры.TypeScript проверяет правильность сигнатуры.

Это небольшое упражнение по рефакторингу должно продемонстрировать вам еще одну замечательную особенность TypeScript быстрое проектирование системы.

Типы писать очень легко. Их быстро описывать (4 строчки типов против 60 строк фактического компонента), а также они содержат много информации о вашей системе. Таким образом, вы можете написать несколько типов и разработать свою программу, фактически не написав никакого кода. Это сэкономит вам много-много времени на архитектуру.

Первый шаг в проектировании любой системы: определение ваших типов.

Архитектура и планирование с типами TypeScript = быстрое написание кода.

После того как вы определили свои типы, вы можете начать писать логику компонента

Давайте исправим <BrowsersList />, так как он будет обрабатывать новую сигнатуру BrowsersListProps. Мы можем провести рефакторинг <BrowserItem />, так чтобы потребовалось бы только 2 пропса вместо множества. Это сделает код более читаемым, а нас быстрее.

Если у вашего компонента много пропсов, это хороший намёк на возможный рефакторинг.

Этот компонент уже выглядит более читабельным и менее пугающим.

Меньше беспорядка на экране = быстрое написание кода.

Извлечение повторно используемоей логики <UIFriendlyList/> от <BrowsersList/>

Просто наблюдая за компонентом, разработчик испытывает множество разных ощущений. Например, я чувствую, что отображать состояние загрузки для списка в <BrowsersList /> это очень крутая функция. Настолько крутая, что, скорее всего, она будет использоваться в различных компонентах.

Это потенциально очень полезная и многоразовая функция нашего приложения. Но в настоящее время она связана с компонентом <BrowsersList />, и мы не можем повторно использовать её в другом месте. Давай исправим это.

Мы хотим создать новый компонент <UIFriendlyList /> и использовать его вместо <FlatList />. Этот <UIFriendlyList /> сможет отображать состояние загрузки.

Как всегда, мы начинаем с определения некоторых типов:

  • T это аргумент типа. Типы с аргументами называются Дженерик. T для UIFriendlyList <T> то же самое, что arg для функции foo (arg). Если вы хотите построить свой тип из другого типа, используйте Generic. Для получения дополнительной информации ознакомьтесь с этой статьёй Ross Bulat.

Объяснение дженериков в TypeScript

Посмотрите на это крутое решение:

  • Мы определяем типы для наших пропсов UIFriendlyListProps.

  • Мы определяем универсальный тип: список может содержать элементы любого типа.

  • UIFriendlyListProps расширяет FlatListProps из библиотеки React Native с помощью нашей функции индикатора загрузки.

  • Итак, мы определяем тип UIFriendlyListProps как пересечение типов из FlatListProps и {loading ?: boolean}

Насколькоэто круто?

Я доволен этим дизайном, давайте напишем тело этого компонента и переместим его в другой файл UIFriendlyList.jsx

Мы также добавили пустое состояние. Таким образом, пользователь будет уверен, что список пуст и не будет ждать поступления данных. Очень дружелюбная функция пользовательского интерфейса.

<UIFriendlyList /> это компонент, который можно использовать повторно, и он наверняка сэкономит нам время в будущем. С этим компонентом мы стали более быстрым React-разработчиком.

Теперь давайте проверим наш <BrowsersList />:

Вот о чём я говорил. Этот компонент намного проще понять по сравнению с исходным BrowsersList . И у нас есть переиспользуемый компонент <UIFriendlyList />, который наверняка сэкономит нам время. Мы можем пойти еще глубже и предположить, что мы хотим повторно использовать логику ModalWindow + List, но мы на этом остановимся.

Этот процесс извлечения повторно используемых частей бесконечен и требует времени. Вам следует остановиться, если вы чувствуете, что можете провести время более продуктивно в другом месте.

Мы, люди, склонны писать код как форму прокрастинации, откладывая решение сложных проблем, которые у нас есть сейчас, путём решения гипотетических проблем будущего. Justin Travis Waith-Mair. Прекратите писать повторно используемые компоненты React.

Мы закончили с интерфейсом, пора проверить бизнес-логику в хуке useBrowsers().

Рефакторинг бизнес-логики в хуке useBrowsers()

Как и компоненты, мы можем создавать настраиваемые повторно используемые хуки. Это делает код более читаемым, потому что кодовая база разделена на более мелкие, многоразовые блоки. Bikash Paneru. Как лучше писать функциональные компоненты в React.

Давайте проведем рефакторинг useBrowsers(), чтобы вернуть валидный объект BrowsersListProps. Я также отрефакторил loading: теперь пропс будет установлен в true перед запросом и на false после.

Выглядит неплохо, но мы можем пойти дальше и сделать многоразовый строительный блок. Функционал запросить данные и сохранить результат в стейт, пока отображается загрузка выглядит как крутой многоразовый строительный блок. Мы хотим отделить его от useBrowsers().

Как всегда, давайте начнём с определения некоторых типов. Мы хотим сделать хук useFetch(), который сможет хранить полученные данные в стейте, а также включать индикатор загрузки. Мы также хотим определить форму данных, которые мы получаем от API, как FetchBrowsersResults:

Неплохо выглядит, теперь давайте определим тело useFetch() и переместим его в специальный файл useFetch.ts:

Я также добавил нотификации на случай ошибки запроса. Таким образом, пользователь увидит описание ошибки.

useFetch() это многоразовая функция, которая наверняка сэкономит нам время в будущем. С помощью этой функции мы стали более быстрым React-разработчиком.

Теперь займёмся рефакторингом хука useBrowsers():

Сравните с изначальным useBrowsers(), он намного меньше по размеру и прост для понимания.

Я не вижу логики, которую можно было бы использовать повторно, из этого компонента. Итак, давайте двигаться дальше.

4 простых совета, как стать более быстрым React-разработчиком.

С основной частью мы закончили, давайте немного расслабимся. Я дам вам 4 простых совета.

1. Никогда не форматируйте код вручную.

Ваша IDE должна предоставлять вам функцию автоматического рефакторинга кода. Ваш проект на React должен содержать файлы .eslintrc.js и .prettierrc.js. Они настраивают линтинг и стиль кода. Вы должны иметь возможность применять этот стиль, нажав горячую клавишу:

Функция автоматического рефакторингаФункция автоматического рефакторинга

2. Никогда не импортируйте модули вручную.

Ваша IDE должна предоставлять вам функцию автоматического импорта. Никогда не вводите ../../../ вручную и не тратьте время на ввод/удаление импорта вручную.

Попробуйте функцию автоматического импорта в действии:

Добавление всеъ отсутствующих импортов.Добавление всеъ отсутствующих импортов.

3. Перемещайтесь по проекту как профессионал

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

Посмотрите на эту огромную файловую структуру крошечного мобильного приложения:

Огромная файловая структура приложенияОгромная файловая структура приложения

Знаете ли вы, как мучительно пытаться найти нужный файл во вкладках?

Открыто 10 вкладок. Где файл, который я ищу?Открыто 10 вкладок. Где файл, который я ищу?

Ваша IDE должна предоставлять вам следующие полезные инструменты:

  • перейти к файлу с помощью строки поиска;

  • перейти к компоненту с помощью строки поиска;

  • перейти к ранее открытому файлу;

  • перейти к определению компонента с помощью курсора;

  • перейти к использованию компонента.

Изучите горячие клавиши своей IDE. Это сделает вашу работу гладкой, как масло.

4. Используйте линтинг ESLint

Каждый React-разработчик знает о боли неправильной зависимости хуков useEffect/Memo/Callback. В них всегда сложно найти ошибки:

Найти эти ошибки без линтинга очень сложноНайти эти ошибки без линтинга очень сложно

ESLint позволяет без труда кешировать эти ошибки.

Инструменты разработки, такие как eslint и typescript, помогают поддерживать кодовую базу в большой команде. Хороший разработчик умеет программировать. Хороший разработчик умеет работать в команде. Roman Nguyen. Создание архитектуры вашего React приложения. Перспективы разработки и бизнеса, о чём следует знать.

Все эти функции доступны в IDE Webstorm. Я рекомендую вам использовать Webstorm для разработки на TypeScript с React.

Заключение: искусство разработки повторно используемых строительных блоков

Искусство быстрого разработчика это искусство писать повторно используемые строительные блоки.

Чтобы быть быстрым, вам необходимо:

  • Отделить бизнес-логику от интерфейса.

  • Использовать TypeScript, чтобы получать меньше ошибок.

  • Использовать TypeScript для включения мощных функций IDE: линтинга, рефакторинга имен, автозаполнения.

  • Определять типы перед написанием кода для быстрого проектирования архитектуры.

  • Разделять свой код на множество небольших файлов многократного использования, чтобы уменьшить беспорядок.

  • С помощью IDE форматировать код и импортировать модули.

  • Переходить по файлам с помощью горячих клавиш.

  • Практика рефакторинга.

Мы успешно отрефакторили компонент <Browsers />. Посмотрите на исходный компонент: это огромный, сложный для понимания монолит. Такой код кажется тяжёлым и замедляет нас.

Взгляните на эту лёгкую, как пёрышко, красоту:

Мы сделали простой для понимания компонент + извлекли 2 очень удобные многоразовые детали:

  • useFetch();

  • <UIFriendlyList />.

Мы можем использовать этот функционал в разных местах нашего приложения и, возможно, даже в разных приложениях. Это наверняка ускорит процесс разработки.

Поэкспериментируйте с кодом из статьи

Посмотрите итоговый результат в Codesandbox, чтобы поэкспериментировать и узнать больше. Этот код написан на React, он немного отличается от кода React Native в статье.

Что дальше?

  • Много читать.

  • Много практиковаться.

  • Задавать вопросы. Особенно под этой статьёй в разделе комментариев.

И последнее, но самое важное:

  • Автоматически тестируйте компоненты React.

  • Практика TDD.

Начать тестировать ваш код сложно, но это сделает вашу жизнь сладкой, как леденец. Посмотрите статью Ian Wilson "Как создавать надёжные приложения React с TDD и библиотекой тестирования React", это хорошая точка для начала изучения тестирования. Ежедневно тренируйте свое ремесло и со временем вы станете мастером. А также не забывайте осваивать новое (например с нашей помощью) только так можно сохранять свою актуальность в стремительно изменяющемся мире технологий. А наши менторы и чуткая поддержка в этом помогут.

Узнайте, как прокачаться в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Локализуем приложение на React Native

11.09.2020 02:18:44 | Автор: admin
В ходе разработки одного из наших приложений нам понадобилось сделать поддержку мультиязычности. Задача была дать пользователю возможность менять язык(русский и английский) интерфейса приложения. При этом текста и контент должны переводиться на лету.

Для этого нам нужно было решить 2 задачи:
1. Определить текущий язык приложения.
2. Использование глобального состояния для перевода на лету.

В этой статья попробую подробно расписать как мы решили данные задачи. И так поехали.

Определяем текущий язык устройства


Для определения текущего языка можно, конечно, воспользоваться библиотекой react-native-i18n, но мы решили обойтись без него, так как для решения этой задачи можно и без сторонних библиотек. Для этого пишем следующее:

import {NativeModules, Platform} from 'react-native';let deviceLanguage = (Platform.OS === 'ios'        ? NativeModules.SettingsManager.settings.AppleLocale ||          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13        : NativeModules.I18nManager.localeIdentifier

Для ios мы извлекаем язык приложения через SettingsManager, а для android через нативный I18nManager.

Теперь когда мы получили текущий язык устройства, мы можем сохранить его в AsyncStorage и приступить ко второй задаче.

Переводим на лету


Для управления глобальным состоянием мы используем MobX, но вы можете использовать другое решение.

И так мы должны создать класс(мне нравится называть модель), который будет отвечать за глобальное состояния текущей локализации. Создаем:

// ключ локального хранилища, в котором будем записывать текущий langconst STORE = '@lang-store';// список русскоязычных стран const RU_LANGS = [  'ru',  'az',  'am',  'by',  'ge',  'kz',  'kg',  'md',  'tj',  'tm',  'uz',  'ua',];class LangModel {  @observable  lang = 'ru'; // по умолчанию  constructor() {    this.init();  }  @action  async init() {    const lang = await AsyncStorage.getItem(STORE);    if (lang) {      this.lang = lang;    } else {      let deviceLanguage: string = (Platform.OS === 'ios'        ? NativeModules.SettingsManager.settings.AppleLocale ||          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13        : NativeModules.I18nManager.localeIdentifier      ).toLowerCase();      if (        RU_LANGS.findIndex((rulang) => deviceLanguage.includes(rulang)) === -1      ) {        this.lang = 'en';      }      AsyncStorage.setItem(STORE, this.lang);    }}export default new LangModel();


При инициализации нашей модели мы вызываем метод init, который берет локаль либо из AsyncStorage, если там есть, либо извлекаем текущий язык устройства и кладет в AsyncStorage.

Далее нам нужно написать метод(action), который будет менять язык:

  @action  changeLang(lang: string) {    this.lang = lang;    AsyncStorage.setItem(STORE, lang);  }


Думаю, что тут все понятно.

Теперь самое интересное. Сами переводы мы решили хранить простым словарем. Для этого создадим js файл рядом с нашей LangModel, в котором мы поместим наши переводы:

// translations.js// Да, за основу мы взяли русский. export default const translations = {  "Привет, Мир!": {en: "Hello, World!"},}


Далее реализуем еще один метод в LangModel, который будет принимать на вход текст и возвращать текст текущей локализации:

import translations from './translations';  ...  rk(text) {    if (!text) {      return text;    }    // если локаль ru, то переводить не нужно    if (this.lang === 'ru') {      return text;    }    // если перевода нет, кинем предупреждение     if (translations[text] === undefined || translations[text][this.lang] === undefined) {      console.warn(text);      return text;    }    return translations[text][this.lang];  }


Все, наш LangModel готов.

Полный код LangModel
import {NativeModules, Platform} from 'react-native';import {observable, action} from 'mobx';import AsyncStorage from '@react-native-community/async-storage';import translations from './translations';const STORE = '@lang-store';// список ru локали const RU_LANGS = [  'ru',  'az',  'am',  'by',  'ge',  'kz',  'kg',  'md',  'tj',  'tm',  'uz',  'ua',];class LangModel {  @observable  lang = 'en';  constructor() {    this.init();  }  @action  async init() {    // Берем текущую локаль из AsyncStorage    const lang = await AsyncStorage.getItem(STORE);    if (lang) {      this.lang = lang;    } else {      let deviceLanguage: string = (Platform.OS === 'ios'        ? NativeModules.SettingsManager.settings.AppleLocale ||          NativeModules.SettingsManager.settings.AppleLanguages[0] // iOS 13        : NativeModules.I18nManager.localeIdentifier      ).toLowerCase();      if (        RU_LANGS.findIndex((rulang) => deviceLanguage.includes(rulang)) > -1      ) {        this.lang = 'ru';      }      AsyncStorage.setItem(STORE, this.lang);  }  @action  changeLang(lang: string) {    this.lang = lang;    AsyncStorage.setItem(STORE, lang);  }  rk(text) {    if (!text) {      return text;    }    // если локаль ru, то переводить не нужно    if (this.lang === 'ru') {      return text;    }    // если перевода нет, кинем предупреждение     if (translations[text] === undefined || translations[text][this.lang] === undefined) {      console.warn(text);      return text;    }    return translations[text][this.lang];  }}export default new LangModel();



Теперь мы можем использовать метод rk для локализация текста:

<Text>{LangModel.rk("Привет, Мир!")}</Text>


Посмотреть как это работает можно в нашем приложении в AppStore и Google Play (Нажать на иконку (!) справа вверху, пролистать вниз)

Бонус


Конечно, писать каждый раз LangModel.rk это не круто. Поэтому мы можем создать собственный компонент Text и в нем уже использовать LangModel.rk

//components/text.jsimport React from 'react';import {Text} from 'react-native';import {observer} from 'mobx-react';import {LangModel} from 'models';export const MyText = observer((props) => (   <Text {...props}>{props.notTranslate ? props.children : LangModel.rk(props.children)}</Text>));


Так же нам может понадобиться, например, менять логотип приложения в зависимости от текущей локализации. Для этого можно просто менять контент в зависимости от LangModel.lang (не забудьте обернуть ваш компонент в observer(MobX))

P.S.: Возможно такой подход вам покажется не стандартным, но он нам понравился больше чем то, что предлагает react-native-i18n

На этом у меня все. Всем спасибо!)
Подробнее..

Перевод Как выбрать мобильную кросс-платформу в 2021 году

25.02.2021 20:10:38 | Автор: admin

Кросс-платформенные решения - тренд в мобильной разработке. Уже есть различные технологии от PWA до Flutter и Kotlin Multiplatform. Как выбрать среди них?

Новый развивающийся бизнес зачастую в первую очередь ориентируется на мобильные технологии: социальные сети, необанкинговые решения, приложения для электронной коммерции, такси и другие. Новый бизнес ориентирован на экономическую эффективность, поэтому переход на кросс-платформенность для разработки мобильного приложения кажется правильным выбором. Посмотрим, что будет в 2021 году и как выбрать правильную технологию.

Познакомимся с Женей

Евгения солюшн-архитектор. Она должна решить, как построить новое мобильное приложение для изучения английского языка не носителями: людьми из Турции, Италии или России. Давайте посмотрим, как Женя подходит к этой задаче.

Приложение должно включать в себя богатую анимацию, уметь воспроизводить и записывать аудио, показывать видео с субтитрами, а также статические и динамические изображения.

В компании владельце приложения также ожидают, что разработкой будет заниматься единая команда как для Android- и iOS-приложений, чтобы свести к минимуму усилия по передаче знаний и максимизировать скорость команды. В будущем также планируют запустить веб-приложения. А еще в компании хотели бы упростить будущий найм.

Прогрессивные веб-приложения

Женя начинает свои исследования. Она гуглит мобильные веб-приложения и находит статью. В ней упоминаются Прогрессивные веб-приложения (PWA). Что это такое?

Прогрессивные веб-приложения это, по сути, веб-сайты, которые используют специальные API для доступа к определенным возможностям устройства. Эти API позволяют получить доступ к памяти на устройстве, интегрируются с Push Notifications (на Android) и, что самое важное, работать в отдельной вкладке браузера. Еще их можно установить на устройство иконкой, как настоящее приложение. Звучит неплохо! Давайте посмотрим на плюсы и минусы PWA:

Плюсы:

  • Всем занимается одна команда.

  • Достаточно навыков веб-разработки.

  • Легко обслуживать.

  • Все работает сразу из коробки.

Минусы:

  • Нет пушей на iOS.

  • Неудобный UX.

  • Ограниченная поддержка.

  • Трудно найти пользователи скачивают приложение в сторах.

А вот и хорошая табличка, показывающая доступность PWA в разных браузерах:

Гибридные приложения

Жене не нравится, что PWA обладают ограниченной поддержкой пушей. Поэтому она гуглит дальше.

Она обнаруживает гибридный подход: давайте поместим наш сайт в WebView в реальном нативном приложении. Оказывается, можно поместить свой мобильный сайт со всем его HTML, CSS и JS-кодом в ресурсы приложения. Затем вы помещаете WebView в корневой ViewController/Activity приложения и указываете на эти ресурсы.

Пользователи смогут найти приложение в Google Play и AppStore, чего не было в случае с PWA. А еще, если вам нужны дополнительные возможности, вы можете легко добавить их ведь у вас настоящее приложение, для этого уже есть необходимые фреймворки.

Плюсы:

  • Высокая скорость работы.

  • Настоящая кросс-платформенность.

  • Можно пользоваться практически всеми преимуществами нативных приложений.

Минусы:

  • UX не очень (ведь перед нами по-прежнему сайт в шкуре приложения).

  • Небольшое сообщество разработчиков.

Архитектура гибридного приложенияАрхитектура гибридного приложения

Примеры таких приложений: Appcelerator, Ionic, Apache Cordova.

Нативные кросс-платформенные приложения

Большой недостаток гибридных приложений плохой UX. Поскольку это все еще браузер в мобильном приложении, он подтормаживает.

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

Xamarin

Xamarin это решение на основе .NET от Microsoft.

Xamarin (или NET 5.0) помогает кросс-платформенное приложение на C# и .NET в мобильном приложении. Xamarin Forms (или MAUI) это библиотека для построения пользовательского интерфейса в таких приложениях. Теперь они оба переименованы:

Плюсы:

  • Легко разрабатывать в компаниях с сильной экспертизой в технологиях Microsoft.

  • Полностью кросс-платформенное.

  • Потенциально можно сделать его и веб-приложением.

Минусы:

  • 2 виртуальные машины в приложении на Android - JVM и Mono

  • Мало инструментов и библиотек

  • Долгое время исправления багов в Xamarin

  • Растет размер бандлов

React Native

React Native создан для того, чтобы привнести React в мобильные технологии. И это действительно хорошо сработало! Согласно статистике Bitrise, React Native занимает 13% рынка. Его также используют более 500 тысяч репозиториев на Гитхабе.

React Native использует языки Javascript или TypeScript для разработки приложений и транслирует компоненты React Native в компоненты платформы. Так, превращается в TextView на Android.

Приложение включает в себя JavaScript VM для выполнения JS-кода логики и пользовательского интерфейса приложения. React Native также умеет работать с API платформы. С такой архитектурой можно создать любое приложение, даже мобильную игру.

Плюсы:

  • Большое сообщество.

  • Пользуются в Skype, Discord, Shopify, Tesla, Artsy.

  • Доступны нативные возможности приложений.

  • Можно писать на React.

Минусы:

  • Долгое время запуска.

  • Экстеншены и работа в фоне требует написания нативного кода.

  • Проходит ре-архитектуризацию.

Flutter

Flutter это кросс-платформенный фреймворк с открытым исходным кодом от Google, основанный на языке Dart. В отличие от React Native, Flutter использует свой собственный 2D-движок для отрисовки пользовательского интерфейса в нативном виде. Такой подход обеспечивает независимость от версии ОС, на которой работает приложение. Flutter приложение также компилирует Dart в нативный для платформы код в релизной сборке, таким образом в рантайме Dart VM становится не нужна. Советую нашей Жене мое подробное сравнение Flutter и React Native.

Flutter привлек к себе большое внимание, собрал более 100 000 звезд, а количество приложений, собранных с его помощью, продолжает стремительно расти. Согласно той же статистике Bitrise, уже 9% билдов в прошлом году собраны на Flutter. Это очень много для такой молодой технологии.

Плюсы:

  • Хорошая производительность

  • Декларативный UI

  • Поддерживает веб

Минусы:

  • Ненативный UI

  • Малопопулярный язык Dart.

  • Недостаток некоторых инструментов (например, в плане контроля безопасности).

Kotlin Multiplatform

Xamarin, Flutter и React Native позволяют вам написать практически весь код единожды и запускать его и на iOS, и на Android.

Kotlin Multiplatform делает иначе. KMP считает, что пользовательский интерфейс сильно зависит от платформы и самого устройства (скажем, планшета или веб-сайта). Однако бизнес-логика остается практически неизменной. Почему бы не переиспользовать прежде всего ее?

Итак, с KMP у вас все еще есть два нативных приложения, которые пользуются одной бизнес-логикой. Вы можете использовать все, что угодно в пользовательском интерфейсе: будь то родной Android Views, JetPack Compose или Swift UI для iOS. Вы даже можете использовать Flutter или React Native для вашего пользовательского интерфейса! Он все равно будет прекрасно работать с Kotlin Multiplatform. Вот несколько примеров.

Плюсы:

  • Родной язык для разработчиков Android.

  • iOS разработчикам нетрудно читать Kotlin код.

  • Единая среда разработки для Android и iOS.

  • Подходит для реализации веб-приложений.

Минусы:

  • Молодое решение, все еще в альфа-версии.

  • Нельзя переиспользовать UI.

Что же выбрать?

Итак, наша Женя собирается выбрать свое решение. Вот что мы ей посоветуем:

  • Согласна на мобильный сайт вместо приложения, несмотря на ограничения в UI? Тогда PWA.

  • В компании все пишут на .NET? Значит, Xamarin.

  • Основной стек это JS/TypeScript? Выбираем React Native.

  • Будем шарить между приложениями не UI, а только бизнес-логику? Значит, Kotlin Multiplatform.

Конечно, это не все критерии: например, мы не рассмотрели доступность разработчиков на рынке или темы поддержки новых версий OS. Но основной алгоритм - выше :)

Удачи, Женя!

Подробнее..

Калькулятор на телефон как способ знакомства с React-native

17.01.2021 22:08:31 | Автор: admin

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

<cut />

Введение

Ввиду того, что на момент планированного перехода в мобильную разработку основным инструментов разработки для меня являлся ReactJS, было принято решение начать именно с него. Кроме того, мною была использована платформа для сборки приложений Expo, которая решила существенную часть проблем с конфигурацией - сборка приложения осуществлялась буквально за несколько команд.
Первым относительно серьезным(по крайней мере, работающим, а не показывающим работоспособность QuickStart`a) приложением для меня стал калькулятор - примерно такой же, как есть в каждом из смартфонов. Калькулятор должен был содержать один экран, в котором есть 2 раздела - дисплей и клавиатура, дисплей - отображает информацию, а клавиатура позволяет ее вводить
Скриншоты получившегося приложения расположены под спойлером. Признаю, выглядит не как шедевр дизайна, но - не сильно хуже встроенного калькулятора. Интерес тут представляет не это

Spoiler
Горизонтальное отображениеГоризонтальное отображениеВертикальное отображениеВертикальное отображение

Для запуска используется онлайн-версия эмулятора, но на нескольких домашних устройствах с андроидом, а так же - в браузерной версии Экспо оно выглядит приблизительно также.

Анализ исходного кода

Для того, что бы приложение было по минимуму "захардкодженным" я вынес все кнопки, которые должны быть на клавиатуре в отдельный двумерный массив. При этом, ввиду того, что надпись на каждой кнопке уникальна - она же используется и в качестве id, т.е. именно по ней привязывается функция-обработчик.

let labels=[    ["1","2","3"],    ["4","5","6"],    ["7","8","9"],    ["0", ".","+/-"],    ["+","-","*","/"],    ["ln","C", "=",]]

Данный подход позволяет не только изменять расположение клавиш, но еще и - в случае необходимости - получать их с сервера, не меняя логику самого приложения.
Аналогичным образом сделана и привязка операций к кнопкам - при этом надпись на кнопке является ключом, а функция, в моем случае стрелочная - значением. Ввиду объемности следующего объекта - уберу его под спойлер. Для взаимосвязи использовались хуки состояний - об этом упомяну чуть позже.

Spoiler
let functionMapping = {        "+":()=>{            setOperation(()=>(a,b)=>{return a+b})            setFirstOperand(display);            setDisplay("")        },        "-":()=>{            setOperation(()=>(a,b)=>{return a-b})            setFirstOperand(display);            setDisplay("")        },        "*":()=>{            setOperation(()=>(a,b)=>{return a*b});            setFirstOperand(display);            setDisplay("")        },        "/":()=>{            setOperation(()=>(a,b)=>{return a/b});            setFirstOperand(display);            setDisplay("");        },        "ln":()=>{            setOperation(()=>(a,b)=>{return Math.log(a)});            setFirstOperand(display);        },        "C":()=>{            setFirstOperand("");            setsecondOperand("");            setOperation(null);            setDisplay("");        },        "+/-":()=>{            setDisplay((+display)*(-1)+"");        },        ".":()=>{            if (display.indexOf(".")===-1)                setDisplay(display+".")        },        "=":()=>{            setsecondOperand(display);            let rezult = operation(+firstOperand, +display);            setDisplay(rezult);        }    }    for (let i =0; i<10; i++) {        functionMapping[i+""]=()=>{setDisplay(display+i)};    }

Тут особого внимания заслуживают конструкции вида

setOperation(()=>(a,b)=>{return a * b})


Выглядит крайне нелогично, кроме того - ошибка, которая выбрасывается при использовании интуитивного

setOperation(()=>{return ab})

весьма посредственно гуглится.
Однако, в статье рассматривается данная проблема - не только ее решение, но и причины, почему это было сделано именно так.
Кроме того, для числовых значений кнопок функция-хэндлер однотипна, поэтому добавление ее обработчиков вынесено в отдельный цикл.

В моем проекте используются функциональные компоненты, поэтому вместо громоздких выражений с конструкторами для хранения требуемой информации используются 4 переменных состояния, одна из которых к тому же не момент релиза не используется (однако умышленно оставлена, ввиду планов на дальнейшее развитие)

    const [operation, setOperation] = useState(null);    const [firstOperand, setFirstOperand] = useState("");    const [secondOperand, setsecondOperand] = useState("");    const [display, setDisplay] = useState("");

Соответственно, firstOperand и secondOperand хранят значения переменных, display отвечает за информацию, выводимую на экран, а operation хранит ключ выбранной операции.
На этом вся логика приложения закончилась, все что происходит дальше - лишь отображение компонентов и прикрепление таблицы стилей
Прикреплю это под спойлер, кроме того, в конце статьи будет ссылка на репозиторий со всеми исходниками.

Spoiler

Отображение компонентов

<View style={styles.root}>      <View style = {styles.display}>          <Text style={{fontSize: 40}}>              {display}          </Text>      </View>        <View style={styles.keyboard}>        {labels.map((value, index, array)=>{          return <View style={styles.row}>              {value.map((item)=>{                  return <TouchableOpacity style={styles.cell} onPress={()=>{functionMapping[item]()}}>                      <Text style={{fontSize: 35}}>{item}</Text>                  </TouchableOpacity>              })}          </View>        })}      </View>    </View>

Стили

const styles = StyleSheet.create({  root: {      flex: 1,      backgroundColor: '#fff',      alignItems: 'center',      justifyContent: 'center',      fontSize:40  },  display:{      flex: 2,      backgroundColor: "lightblue",      width: "100%",      justifyContent: "space-around",      alignItems: "center"  },  keyboard:{      flex: 9,      width: "100%",      backgroundColor:"lightgrey",      justifyContent:"space-around",      alignItems:"center"  },  row:{      flex: 1,      flexDirection:"row",      justifyContent:"space-around",      alignItems:"center",      width:"100%",  },  cell:{      flex:1,      borderWidth:1,      width:"100%",      height:"100%",      justifyContent:"space-around",      alignItems:"center",    }});

Процесс написания и отладки-ощущения

Приложение создавалось мною с помощью Expo quickstart. Она создает базовое приложение, имеющее все основные разделы и базовый файл App.js, который предполагается изменять для получения желаемого результата. Проблем с конфигурацией практически нет - в этом большой плюс, т.к. попытки разобраться с созданием мобильных приложений на Java разбивались в том числе и о сложности конфигурации (это была не единственная причина, но с конфигами Джавы у меня исторически сложились напряженные отношения). Любое изменение практически сразу же можно увидеть на web-сервере, запускаемым Expo, либо - в эмуляторе.
Однако тут есть несколько проблем. Большую часть времени я отлаживал приложение в браузере, запуская эмулятор андроида только перед тем как отправить на сборку релизной версии. И - возникла очень не очевидная проблема, когда один и тот же код корректно работал на веб-версии и напрочь вылетал на андроиде, не оставляя никаких логов. Методом последовательного исключения компонентов было выявлено, что проблема в компоненте Picker, который ожидал получить в качестве называния строку, а получал - число. (данный элемент не попал в релизную версию программы, но не отметить я это не мог). Кроме того, в веб-версии шрифты можно было изменять с помощью поля строкового типа, задавая ему габариты в условно любых единицах - процентах, пикселях или даже архаичных "сантиметрах", в то время как на андроиде программа требовала дать ей только число.
Очень не порадовало отсутствие по умолчанию наследования свойств - нельзя было задать шрифт и выравнивание для корневого элемента, полагая, что все его потомки будут его поддерживать.
С другой стороны - очень к месту приходится возможность flex разметки, которая в вебе появилась сравнительно недавно - именно с ее помощью получается получать довольно аккуратную верстку даже на довольно экстремальных соотношениях сторон. С помощью "классического" CSS, безусловно, можно сделать то же самое, но это потребует на порядок более кропотливой работы.
Ну и безусловно - размер сборки получается слишком весомый. Я понимаю, что это плата за простоту написания, и на чем-либо уровня С, размер приложения будет существенно меньше, но тем не менее...

Выводы

В целом, учитывая опыт верстки на чистом Реакте, освоение основных принципов React-native заняло очень небольшое количество времени. Инструмент содержит в себе основные преимущества Реакта, однако - не клонирует его полностью, внося ряд ограничений и добавляя новые возможности. В некотором роде можно сказать, что нативный Реакт более требователен к типам, чем обычный, но если сравнивать его с React over TypeScript - различий становится еще меньше. Как мне кажется, можно сказать, что react-native может стать неплохой точкой входа в индустрию разработки мобильных приложений для лиц, имеющих опыт в вебе.

P.S. Cтатья не претендует на рассмотрение как авторитетное мнение о технологии, она лишь описывает - надеюсь, объективно - первое впечатление от инструмента от человека, имеющего опыт в смежной области
P.P.S Целевая аудитория статьи - не гуру веб разработки, а скорее смежники, подобные мне.

Исходники

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru