Share This
Связаться со мной
Крути в низ
Categories
//Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Что делать, если скорость разработки уже не та, что раньше? Отказавшись от монолита, изменить подход к написанию кода и начать использовать его повторно! Обсудить

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 567e9ac - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

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

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

Современный фронтенд крепко стоит на компонентах, потому что это действительно разумный и приятный подход к разработке. Собирать программу из «кирпичиков Lego» существенно проще и надежнее, чем с нуля писать монолит.

Так бросим же все силы на то, чтобы научиться мыслить в компонентном стиле!

Учимся готовить компоненты

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

Будьте готовы к многочисленным озарениям и головокружительному увеличению вашей производительности!

Исходный компонент

Рассмотрим неограненный алмаз, с которым мы будем работать. Этот простой компонент умеет не так уж и много:

  1. он получает с бэкенда список браузеров;
  2. отображает индикатор загрузки, пока запрос выполняется;
  3. выводит полученные данные в виде карточек;
  4. если пользователь нажимает на карточку, компонент показывает модальное окно с детальной информацией о браузере.

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

Browsers.js

         import React, { useEffect, useState } from 'react'; import { FlatList, Text, View, StyleSheet, Modal, TouchableOpacity } from 'react-native';  import AddModal from '../components/AddModal'; import LoadingIndicator from '../components/LoadingIndicator' import BrowserItem from '../components/BrowserItem'  import colors from '../config/colors';  function Browsers() {      const URL = 'https://google.com/myData.json'      // Элемент массива     // {"Browsers":[     //     {     //      "fullname": "Chrome",     //      "linkToBrowser": "https://google.com",     //      "image": "https://linktoimage.com/chrome.png",     //      "minMemory": "1 GB",     //      "currentVersion": "29.0.1",     //      "minimumRAM": "2 GB",     //      "description": "How much RAM do you have? Ha-ha",     //      "windows": true,     //      "mac": true,     //      "linux": true,     //      "ubuntu": true,     //      "fedora": false,     //      "stars": 4,     //      "id":"chrome"     //    },     // ...     // ]     // }      const [loading, setLoading] = useState(true)     const [browsers, setBrowsers] = useState([])      const [modalVisible, setModalVisible] = useState(false)     const [description, setDescription] = useState("")      const changeDescription = (description) => {         setDescription(description)         setModalVisible(!modalVisible)     }      const changeOpacity = () => {         setModalVisible(!modalVisible)         console.log('changeOpacity')     }        useEffect(() => {         fetch(URL)             .then((response) => response.json())             .then((responseJson) => {                 return responseJson.Browsers             })             .then(browsers => {                 setBrowsers(browsers)                 // console.log(browsers)                 setLoading(false)             })             .catch(error => {                 console.log(error)             })             .finally(() => setLoading(false));     }, [])       return (         <View style={styles.container}>             {loading ? (                 <LoadingIndicator />             ) : (                     <View>                         <AddModal                              modalVisible={modalVisible}                             changeOpacity = {() => changeOpacity()}                             description={description}                         />                         <FlatList                             data={browsers}                             keyExtractor={browser => browser.fullname}                             renderItem={({ item }) =>                                 <BrowserItem                                     fullname={item.fullname}                                     image={item.image}                                     linkToBrowser={item.linkToBrowser}                                     minMemory={item.minMemory}                                     currentVersion={item.currentVersion}                                     minimumRAM={item.minimumRAM}                                     description={item.description}                                     windows={item.windows}                                     mac={item.mac}                                     linux={item.linux}                                     ubuntu={item.ubuntu}                                     fedora={item.fedora}                                     stars={item.stars}                                     changeDescription={() => changeDescription(item.description)}                                 />                             }                         />                     </View>                 )             }         </View >     ); };  const styles = StyleSheet.create({     container: {         justifyContent: 'center',         alignItems: 'center'     }, })  export default Browsers;     

Что с этим компонентом так?

Ну, он работает 🙂 И использует хуки, что само по себе уже неплохо.

Что с этим компонентом не так?

Это монолит. Не получится взять его и переиспользовать в другом месте или другом приложении.

К чему мы стремимся?

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

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

  • Не нужно повторять одно и то же по десять раз – помним о принципе DRY!
  • Не нужно вносить изменения в нескольких местах – и лихорадочно вспоминать, где еще есть этот функционал.
  • Не нужно ловить баги по всему приложению – они смирно сидят в компоненте и ждут вас.
  • Не нужно много читать, код сразу становится короче, чище и понятнее.
  • Не нужно писать тесты для каждого места, в котором используется функциональность.

Приступаем к огранке

Начнем с самых простых приемов рефакторинга, а затем войдем во вкус и сдеаем что-то посложнее.

Шаг #1. Перенос констант в пропсы

Browsers.js

         function Browsers({url = 'https://google.com/myData.json'}) {    const URL = url   ...     

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

Шаг #2. Разделение бизнес-логики и отображения

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

В чем вообще разница?

Бизнес-логика: Код, который принимает решения и изменяет состояния. Все внутри <Browsers/> до инструкции return.

Логика отображения: код, который отображает состояние приложения на экране и взаимодействует с пользователем. Все внутри инструкции return в нашем примере.

Давайте произведем радикальное рассечение компонента на две части и убедимся, что жизнь действительно стала проще:

  • Пользовательский хук useBrowsers() с бизнес-логикой.
  • Компонент <BrowsersList /> с логикой отображения.

Browsers_splitted.js

         import React, {useEffect, useState} from 'react' import {FlatList, StyleSheet, View} from 'react-native'  import AddModal from '../components/AddModal' import LoadingIndicator from '../components/LoadingIndicator' import BrowserItem from '../components/BrowserItem'  const styles = StyleSheet.create({   container: {     justifyContent: 'center',     alignItems: 'center',   }, })  function useBrowsers(url) {   const [loading, setLoading] = useState(true)   const [browsers, setBrowsers] = useState([])    const [modalVisible, setModalVisible] = useState(false)   const [description, setDescription] = useState('')    const changeDescription = (description) => {     setDescription(description)     setModalVisible(!modalVisible)   }    const changeOpacity = () => {     setModalVisible(!modalVisible)     console.log('changeOpacity')   }    useEffect(() => {     fetch(URL)       .then((response) => response.json())       .then((responseJson) => {         return responseJson.Browsers       })       .then((browsers) => {         setBrowsers(browsers)         // console.log(browsers)         setLoading(false)       })       .catch((error) => {         console.log(error)       })       .finally(() => setLoading(false))   }, [])    return {     loading,     browsers,     modalVisible,     description,     changeDescription,     changeOpacity,   } }  function BrowsersList({   loading,   browsers,   modalVisible,   description,   changeDescription,   changeOpacity, }) {   return (     <View style={styles.container}>       {loading ? (         <LoadingIndicator />       ) : (         <View>           <AddModal             modalVisible={modalVisible}             changeOpacity={() => changeOpacity()}             description={description}           />           <FlatList             data={browsers}             keyExtractor={(browser) => browser.fullname}             renderItem={({item}) => (               <BrowserItem                 fullname={item.fullname}                 image={item.image}                 linkToBrowser={item.linkToBrowser}                 minMemory={item.minMemory}                 currentVersion={item.currentVersion}                 minimumRAM={item.minimumRAM}                 description={item.description}                 windows={item.windows}                 mac={item.mac}                 linux={item.linux}                 ubuntu={item.ubuntu}                 fedora={item.fedora}                 stars={item.stars}                 changeDescription={() => changeDescription(item.description)}               />             )}           />         </View>       )}     </View>   ) }  function Browsers() {   return <BrowsersList {...useBrowsers('https://google.com/myData.json')} /> }  export default Browsers     

Код все еще далек от идеала, но уже стал немного более удобным.

  • <BrowsersList /> теперь можно использовать с разными источниками данных (а не только с данными из HTTP-эндпоинтов).
  • useBrowsers() теперь может работать с разными представлениями или даже вообще без представления. Если вы измените дизайн приложения, этот хук продолжит работать.

Шаг #3. Разделение на файлы

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

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 49813f8 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Это файловая структура типичного React-проекта:

  1. index.js экспортирует <Browsers/> из Browsers.jsx;
  2. строительные блоки для <Browsers/> лежат в папках components и hooks;

BrowsersList.jsx можно развить до самостоятельной папки с собственными хуками, компонентами и файлом index.js

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 1b91126 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Посмотрим на BrowsersList.jsx:

         import React from 'react' import {FlatList, StyleSheet, View} from 'react-native' import AddModal from '../../../components/AddModal' import LoadingIndicator from '../../../components/LoadingIndicator' import BrowserItem from '../../../components/BrowserItem'  const styles = StyleSheet.create({   container: {     justifyContent: 'center',     alignItems: 'center',   }, })  export function BrowsersList({   loading,   browsers,   modalVisible,   description,   changeDescription,   changeOpacity, }) {   return (     <View style={styles.container}>       {loading ? (         <LoadingIndicator />       ) : (         <View>           <AddModal             modalVisible={modalVisible}             changeOpacity={() => changeOpacity()}             description={description}           />           <FlatList             data={browsers}             keyExtractor={(browser) => browser.fullname}             renderItem={({item}) => (               <BrowserItem                 fullname={item.fullname}                 image={item.image}                 linkToBrowser={item.linkToBrowser}                 minMemory={item.minMemory}                 currentVersion={item.currentVersion}                 minimumRAM={item.minimumRAM}                 description={item.description}                 windows={item.windows}                 mac={item.mac}                 linux={item.linux}                 ubuntu={item.ubuntu}                 fedora={item.fedora}                 stars={item.stars}                 changeDescription={() => changeDescription(item.description)}               />             )}           />         </View>       )}     </View>   ) }     

Этот код уже почти помещается в один экран вашего редактора. Меньше хлама – легче читать!

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

Большая проблема маленьких файлов

Взгляните на сигнатуру функции <BrowsersList />. Что произойдет, если мы переименуем какой-нибудь пропс, например, changeDescription на setSelectedBrowser? Или удалим аргумент? Или добавим новый?

Все сломается!

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

Ну и где профит-то, спросите вы? Опять что-нибудь где-нибудь забудем исправить – и прощай продакшн.

Посмотрите внимательно, все ли тут в порядке?

Файл useBrowsers.js:

         ... return {     loading,     browsers,     modalVisible,     descripton,     changeDescription,     changeOpacity,   } ...     

Файл BrowsersList.jsx:

         ... export function BrowsersList({   loading,   browsers,   modalVisible,   description,   changeDescription,   changeOpacity, }) { ....     

Бинго! Одна маленькая опечатка – и все идет кувырком.

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

Хватит медленно кодить на JS, давайте кодить быстро на TS

В 2021 году у вас нет оправданий, если вы не используете TypeScript. Вам даже не требуется сразу же изучить все его возможности, ведь можно просто добавить TS в проект, не меняя ни строчки, и постепенно наращивать его применение.

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 2758492 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

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

С TypeScript ваша IDE обогатится несколькими крутыми фичами:

  • Автодополнение для пропсов

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika b754781 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

  • Возможность автоматического переименования пропсов
  • Проверка на null/undefined

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika bfbe81b - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

  • Валидация значений пропсов

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika a8ee978 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

TypeScript сэкономит вам тонну времени и километры нервов.

А вот, кстати, и наша опечатка:

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 5c27528 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

Шаг #4. Определение типов

Так как это не полноценный гайд по TypeScript, разбираться в основах мы сейчас не будем. Все желающие могут заглянуть на typescriptlang.org (или прочитать статью в «Библиотеке программиста» – прим. ред.). Вернемся к <BrowsersList /> и преобразуем его пропсы в симпатичные типы (не забудьте сменить расширение с .jsx на .tsx).

BrowsersList_props_types.tsx

         // ...  export type Browser = {   fullname: string // ожидается, что поле "fullname" - это строка   image: string   linkToBrowser: string   minMemory: string   currentVersion: string   minimumRAM: string   description: string   windows: boolean   mac: boolean   linux: boolean   ubuntu: boolean   fedora: boolean   stars: number }  export type BrowsersListProps = {   loading: boolean   browsers: Browser[] // ожидается, что поле "browsers" это массив элементов с типом Browser   modalVisible: boolean   description: string      // ожидается, что changeDescription - это функция   // которая принимает строку и ничего не возвращает   changeDescription: (description: string) => void      // ожидается, что changeOpacity - это функция   // которая ничего не принимает и ничего не возвращает   changeOpacity: () => void }  export function BrowsersList({   loading,   browsers,   modalVisible,   description,   changeDescription,   changeOpacity, }: BrowsersListProps) {   return (  // ...     

Теперь обновим сигнатуру useBrowsers():

useBrowsers.ts

         import {useEffect, useState} from 'react' import {BrowsersListProps} from '../components/BrowsersList'  export function useBrowsers(url: string): BrowsersListProps {   const [loading, setLoading] = useState(true)     // ...     

И теперь TypeScript будет строго следить, чтобы useBrowsers() и BrowsersList были совместимы.

Быстрая разработка архитектуры

BrowsersListProps выглядит весьма нагруженно:

props.tsx

         export type BrowsersListProps = {   loading: boolean   browsers: Browser[]   modalVisible: boolean   description: string   changeDescription: (description: string) => void   changeOpacity: () => void }     
  • Одна строчка – для состояния загрузки.
  • Одна строчка – для списка браузеров.
  • Целых четыре строчки – для отображения детальной информации о браузере в модальном окне. Есть большая вероятность, что потребуется выводить больше данных, а значит сигнатура может измениться. А это весьма беспокойное дело, как мы помним.

Мы можем уменьшить сложность этого фрагмента и отрефакторить функциональность модального окна для использования типа Browser:

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 09ed38f - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

TypeScript указывает на некорректность сигнатуры при ее изменении

Этот маленький рефакторинг должен продемонстрировать вам замечательную способность TypeScript: быстро проектировать системный дизайн. Писать типы очень просто. Они маленькие, но содержат много полезной информации о вашей системе. Фактически вы можете описать вашу программу без кода, одними типами.

Теперь поправим <BrowserList />, чтобы он мог работать с новой сигнатурой BrowsersListProps.

Отрефакторим <BrowserItem />. Сейчас он принимает множество пропсов, и это явный сигнал для внесения правок. Теперь он будет принимать всего 2 аргумента:

BrowsersList.tsx

         import React from 'react' import {FlatList, StyleSheet, View} from 'react-native' import AddModal from '../../../components/AddModal' import LoadingIndicator from '../../../components/LoadingIndicator' import BrowserItem from '../../../components/BrowserItem'  const styles = StyleSheet.create({   container: {     justifyContent: 'center',     alignItems: 'center',   }, })  export type Browser = {   fullname: string   image: string   linkToBrowser: string   minMemory: string   currentVersion: string   minimumRAM: string   description: string   windows: boolean   mac: boolean   linux: boolean   ubuntu: boolean   fedora: boolean   stars: number }  export type BrowsersListProps = {   loading: boolean   browsers?: Browser[]   selectedBrowser?: Browser   setSelectedBrowser: (browser?: Browser) => void }  export function BrowsersList(props: BrowsersListProps) {   const {loading, selectedBrowser, setSelectedBrowser, browsers} = props   return (     <View style={styles.container}>       {loading ? (         <LoadingIndicator />       ) : (         <View>           <AddModal             modalVisible={Boolean(selectedBrowser)}             onClose={() => setSelectedBrowser(undefined)}             description={selectedBrowser?.description}           />           <FlatList             data={browsers}             keyExtractor={(browser) => browser.fullname}             renderItem={({item}) => (               <BrowserItem                 browser={item}                 onPress={() => setSelectedBrowser(item)}               />             )}           />         </View>       )}     </View>   ) }     

Компонент стал проще и надежнее.

Шаг #5. Извлечение <UIFriendlyList />

Если вы немного помедитируете на компонент, то на вас снизойдет просветление. Список с «состоянием загрузки» – это очень крутая и востребованная функциональность, которая наверняка будет использоваться везде, где только можно.

Сейчас эта функциональность интегрирорована в <BrowsersList />. Значит надо ее извлечь. Создадим новый компонент <UIFriendlyList /> и будем использовать его вместо простого <FlatList/ >.

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

         type UIFriendlyListProps<T> = FlatListProps<T> & {loading?: boolean}      
  • T – это аргумент типа, или дженерик. То же самое, что arg в сигнатуре foo(arg). Дженерики нужны, если вы хотите создать свой тип из другого типа. Вот здесь есть подробнейшее описание дженериков.
  • & – это символ пересечения множеств (интерсекция). Тип X = A & B означает, что X содержит в себе свойства A и B.

Итак, что мы сделали:

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

Теперь вынесем новый компонент в отдельный файл UIFriendlyList.jsx:

UIFriendlyList.tsx

         import React from 'react' import {FlatList, FlatListProps, Text} from 'react-native' import LoadingIndicator from './LoadingIndicator'  export type UIFriendlyListProps<T> = FlatListProps<T> & {loading?: boolean}  export function UIFriendlyList<T>(props: UIFriendlyListProps<T>) {   if (props.loading) {     return <LoadingIndicator />   }    if (props?.data && props.data.length === 0) {     return <Text>This list is empty (</Text>   }    return <FlatList {...props} /> }     

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

Компонент <UIFriendlyList /> – это крутой строительный блок, который очень легко переиспользовать в самых разных ситуациях. Добавьте его в свою коллекцию кирпичиков, чтобы стать еще быстрее.

Во что теперь превратился наш <BrowsersList />:

BrowsersList.tsx

         import React from 'react' import {StyleSheet, View} from 'react-native' import AddModal from '../../../components/AddModal' import BrowserItem from '../../../components/BrowserItem' import {UIFriendlyList} from '../../../components/UIFriendlyList'  const styles = StyleSheet.create({   container: {     justifyContent: 'center',     alignItems: 'center',   }, })  export type Browser = {   fullname: string   image: string   linkToBrowser: string   minMemory: string   currentVersion: string   minimumRAM: string   description: string   windows: boolean   mac: boolean   linux: boolean   ubuntu: boolean   fedora: boolean   stars: number }  export type BrowsersListProps = {   loading: boolean   browsers?: Browser[]   selectedBrowser?: Browser   setSelectedBrowser: (browser?: Browser) => void }  export function BrowsersList(props: BrowsersListProps) {   const {loading, selectedBrowser, setSelectedBrowser, browsers} = props   return (     <View style={styles.container}>       <AddModal         modalVisible={Boolean(selectedBrowser)}         onClose={() => setSelectedBrowser(undefined)}         description={selectedBrowser?.description}       />       <UIFriendlyList         loading={loading}         data={browsers}         renderItem={({item}) => (           <BrowserItem             key={item.fullname}             browser={item}             onPress={() => setSelectedBrowser(item)}           />         )}       />     </View>   ) }     

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

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

Мы закончили с логикой отображения, пора вернуться к бизнес-логике.

Шаг #6. Рефакторинг useBrowsers()

Хук useBrowswers() должен возвращать валидный объект с типом BrowsersListProps:

useBrowsers.ts

         import {useEffect, useState} from 'react' import {Browser, BrowsersListProps} from '../components/BrowsersList'  export function useBrowsers(url: string): BrowsersListProps {   const [loading, setLoading] = useState(false)   const [browsers, setBrowsers] = useState<Browser[]>([])   const [selectedBrowser, setSelectedBrowser] = useState<Browser | undefined>(     undefined,   )    useEffect(() => {     setLoading(true)     fetch(url)       .then((response) => response.json())       .then((responseJson) => {         return responseJson.Browsers       })       .then((browsers) => {         setBrowsers(browsers)       })       .catch((error) => {         console.log(error)       })       .finally(() => setLoading(false))   }, [url])    return {     loading,     browsers,     selectedBrowser,     setSelectedBrowser,   } }     

Приглядимся повнимательнее. Функциональность «обратиться по некоторому URL и сохранить результат запроса, пока отображается индикатор загрузки» кажется весьма полезной, давайте извлечем ее в новый хук useFetch.

Как всегда, сначала типы. Нам нужно, чтобы useFetch сохранял данные из эндпоинта и отображал состояние загрузки. Формат данных будет задан отдельно, для этого используем дженерик:

useFetch.ts

         export type FetchBrowsersResults = {   Browsers: Browser[] }  export type UseFetch<T> = {   loading: boolean      // We use Generic. T - is a type argument that can be any type.   // We can useFetch() with any type   // ? means, that T can be undefined   data?: T }  export function useFetch<T>(url: string): UseFetch<T> {}     

Таким образом, useFetch можно будет использовать с любыми типами данных.

Теперь сама функция:

useFetch.ts

         import {useEffect, useState} from 'react' import {Alert} from 'react-native'  export type UseFetch<T> = {   loading: boolean   data?: T  }  export function useFetch<T>(url: string): UseFetch<T> {   const [loading, setLoading] = useState<boolean>(false)   const [data, setData] = useState<T | undefined>(undefined)    useEffect(() => {     setLoading(true)     fetch(url)       .then((response) => response.json())       .then(setData)       .finally(() => setLoading(false))       .catch((error) => Alert.alert('Fetch error', error))   }, [url])    return {     loading,     data,   } }     

Для полноты картины добавили Alert при ошибке запроса.

Теперь окончательно отрефакторим useBrowsers():

useBrowsers.ts

         import {useState} from 'react' import {Browser, BrowsersListProps} from '../components/BrowsersList' import {useFetch} from '../../../hooks/useFetch'  export type FetchBrowsersResults = {   Browsers: Browser[] }  export function useBrowsers(url: string): BrowsersListProps {   const {loading, data} = useFetch<FetchBrowsersResults>(url)   const [selectedBrowser, setSelectedBrowser] = useState<Browser | undefined>(     undefined,   )    return {     loading,     browsers: data?.Browsers,     selectedBrowser,     setSelectedBrowser,   } }     

Сравните с исходным вариантом.

Кажется, здесь больше нечего извлекать 🙂

4 простых совета для ускорения разработки

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

Используйте возможности IDE, ESLint и Prettier.

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 7160c4e - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

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

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

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika ae2fde5 - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

3. Научитесь ориентироваться среди множества файлов

Разделяя проект на маленькие переиспользуемые кирпичики вы неизбежно приходите к монструозной файловой структуре вроде этой:

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika ab91cbd - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

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

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

Это поможет найти самые хитрые и вредные ошибки:

pishi na react v dva raza bystree prostye trjuki dlja krutogo razrabotchika 9dd251f - Пиши на React в два раза быстрее! Простые трюки для крутого разработчика

***

Искусство быстрой разработки

Чтобы быть быстрым, не потеряв при этом в качестве, вам нужно освоить несколько вещей:

  • Отделять бизнес-логику от отображения.
  • Разделять код на маленькие реюзабельные компоненты.
  • Использовать TypeScript и начинать работу с определения типов.
  • Практиковаться в рефакторинге.
  • Использовать плюшки вашей IDE.

Насколько вы постигли дзен быстрой разработки? Признавайтесь, часто рефакторите?

Дополнительные материалы:

  • Самоучитель для начинающих: как освоить TypeScript с нуля за 30 минут?
  • Демистификация хуков React: useCallback, useMemo и все-все-все
  • 8 мощных библиотек React, которые стоит попробовать в 2021 году
  • 33 приема оптимизации JavaScript, которые вы должны знать в 2021 году

  • 6 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close