Галоўная / Артыкулы / Пастырское зміненне тэмы ў React Native за дапамою Context і Hooks

Пастырское зміненне тэмы ў React Native за дапамою Context і Hooks

Створыце пераключальнік тэм для React Native з викорыстанням Context, useState і useEffect: навігацыя паміж карточкамі, выбір тэмы, зберагачча даных AsyncStorage і елемент захисту пад час запуску.

1618 слоў

Змена тэмы выглядае як проблема стайлізавання, але на самай справе цэла проблема стану: ныячая тэма павінна быць чытаемай са будзь-якага экрана, можна было бы яе зменіць з аднаго з іх, і ўсё равна застаўся пасля перзапуску прыкладнення. Гэта робіць яе хорашым трэнуванням для выучэння Hooks у чымсь рэалістычнейшам, чым проста лічылка. У гэтым кераванні ствараецца маленькая прыкладнае програма на React Native з двумя вікнамі, выбірачом тэмы і постацейным зберагачам дадзеных, пры чым для замены таго, што раней трэбавалася класовыя компоненты і значны час на налагоджэння, викорыстоўваюцца useContext, useState і useEffect.

Чаму Hooks падходяць для рашэння гэтай проблемы

Hooks был адаптаваны ў React 16.8, а React Native пачаў стабільна падтрымліваць іх у версіі 0.59. Да ўведэнняягох карыстоўванне тэмай значыла наявнасць класовага компонента-прадастальніка, які кераваў станам, меў методы жыцёвага циклу для запуску зберажаных настройкаў, а таксама пропсы render чы спожывачы, распакаваныя па всім дрэву компонентаў. За дапамогою Hooks прадастальнік стае звычайным функцыйным компонентам, стан знаходзіцца ў useState, завантажэнне ведзецца ў useEffect, а спожывачы чытаюць значэнне за дапамогою useContext. Логіка тады знаходзіцца ў аднам маленькім файле, замест таго каб была распакаваная па разныя методы жыцёвага циклу.

Указаныя тут версіі паказваюць, калі ўпершыне стала можліва такая падход, тады як сучасныя версіі React Native падтрымлівають Hooks без дапамогі дадатковых налаштаванняў, таму будь-які няўжоўны проект можа іх выкарыстоўваць.

Настройка проекта

Патрэбны проект React Native версіі 0.59 або новейшай. Новы проект можна стварыць за дапамою react-native init RNThemeProvider (у новейшых версіях викорыстоўваецца community CLI або Expo; пераканайцеся ў актуальных інструкцыях па запуску).

Даўно ўключаюцца две бібліятэкі:

  • react-navigation апранае навігацыю на адной з панелей.
  • AsyncStorage храніць выбраны тэму на прыстрое. Ёй было скасавана падтрымкі ў ядре React Native, і тепер яна выпускаецца як окалічны пакет спяльнай спальноты (сёньня публікуецца як @react-native-async-storage/async-storage), таму ёе трэба інсталаваць окрема.

У старэйшых версіях React Native пасля інсталляцыі таксама трэба было ручнае налагоджыць натіўныя модулі; у сучасных версіях з автоналагоджэнням гэты крок зазвычай не патрабуецца.

Арганізацыя файлаў

Малая, прыглядная структура дазволяе выдзеліць логіку тэмы ад экранаў і навігацыі:

- src
  — components
    — TabBar.js # custom bottom tabbar component
  — core
    — themeProvider.js # custom hook for theming
    — themes.json # JSON array containing our themes
  — screens
    — Main.js # first tab
    — Settings.js # second tab
  — App.js # navigation part
— index.js # entry point for react-native
— package.json # dependencies

Папка core зберагае ўсё, што стосуецца тэмавання: themes.json з вызначэннямі тэмаў і themeProvider.js з контекстам, прадастчыкамі і дапаможнымі функцыямі. Экраны знаходзяцца ў папцы screens, спецыяльная панель вкладак у components, а App.js адпавядае за налагоджэння навігацыі.

Стварэнне каркаса навігацыі

Пачніце без якога-лібо тэмавання. У App.js створыце навігатор вкладакоў у нижней частцы з двума вкладкамі: Main для контэнту прыложэння і Settings для налашоўкаў. Кожная вкладка пінае на простую функцыйную компоненту ў Main.js і Settings.js, якая, наразе, адрасавае толькі текст-заместычнік.

У гэтым этапе запускайце прыграму. Якщо з’явіцца два вікна і вы зможыце пераліцца между ямі, значы каркас готавы, а кожны наступны крок толькі дадае додатковыя функцыі. API для навігаціі зменяліся ў розных версіях react-navigation, таму следзьце інструкцыям па версію, яую вы інсталюеце, а не копіюйце старыя прыклады без змян.

Апіранне тэмаў і інтэрфейсу выбору

Тэмы як данні

Кожная тэма — это об’ект у файле themes.json, які мае тры поля: унікальны key, які ўскладнюе ёё, колер фона і колер тексту. Зберагачыце тэмы у формате JSON, а не коду, ўсёлякія — так іх легка расширваць; генератор палітры, такі як Coolors, — гэта прыемны спосаб знайсці пары калераў, якія добра работаюць разам.

themeProvider.js імпортуе тыя файлы і наразе экспортуе два элементы: цэлыя спіс тэмаў, які паказваецца на экране налашоўкаў, і стандартны тэма (другі элемент у спісе). Логіка прадастаўчыка будзе дадаткова реалізавана пазней.

Экраны і паліця вкладакоў

На экране Налашоўкі выкарыстоўваецца FlatList, які адрасавае по аднай строчцы на кожны тэма, пры чым заголовак форматуецца па стылю дзейневага тэма. На экране Галоўны таксама прыменяецца дзейневы тэма да фона і тексту.

На завершанне паліця вкладакоў таксама должна адпраўляць тэма. Спецыяльны компонент TabBar у файле components/TabBar.js адрасавае вкладакі і выкарыстоўвае калеры тэма для актыўнага вкладака. Ён рэгіструецца ў навігаторы вкладакоў у файле App.js чераз параметры навігатора для спецыяльнага компонента паліцы вкладакоў.

У гэты момент аплікэцыя выглядае з тэмай, але толькі з зашыфраваным стандартным варыянтом. Нічога ўсё яшчэ не реагуе на нажатыя. Самэлькі тут і прабуюць Hooks.

Раздзеленне тэмы за дапамою useContext

React Context дазваляе значэнню циркулюваць праз всю структуру без неабяжнага перадачы пропсаў на кожны ўзлак. Якщо Context для вас новы, документацыя React Context пояснюе гэты механізм.

У файле themeProvider.js створыце контэкст тэмы і компонент-прадастальнік ThemeContextProvider. Абгорніце компонент App.js гэтым прадастальнікам, каб усе экраны і панель вкладакоў належалі да яго.

Ёнколі жадаецца, каб выкарыстоўванне контэкста было зручным, да таго ж файлу трэба дадаць компонент вышэйшага порядку withTheme. Ён чытае контэкст за дапамою useContext і перадае тэму ў обгорнуты компонент як проп. Неабходна адкоректаваць Main, Settings і TabBar так, каб яны экспортуваліся через withTheme, і тады яны будуць прымаць чырвонуючую тэму, не ведаючы, звядзе ў яе падачу. У нарадзе па компонентах вышэйшага порядку даны болей дакладныя інструкціі па гэтым патэрне.

HOC добраўся працуе тады, калі компаненты вясна чакаюць пропсы. У кодбэсе, дзе пераважаюць хукі, невялікі хук useTheme, який вяртае useContext(ThemeContext), часта ўсё простыяй, узбегае дапамогі дадатковага абгорткавага шару і робіць залежнасць видной унутры самага компаненту. Наша інструкцыя па спецыяльных хукіх і павторным викорыстоўванню логікі поясняе, чаму хукі дзелюцца логікай, а не станам, і самэ гэта ўсё ж прычына, чаму тут яшчэ патрэбен контекст.

Змена тэмы за дапамогою useState

Тепер нехай пікер працавае. Унутры ThemeContextProvider зберагаюце текущую тэму ў useState, ініцыялізаванага стандартной тэмай. У значэнне контексту разам з тэмаю ставяце таксама функцыю setTheme.

У экране налашчання вызывайце setTheme, калі нажата яка-небудзь строчка ў FlatList. Паколькі стан прадаўцы зміняецца, кожны компонент, які чытае контекст, перерабляецца з новымі кольрамі: экраны і панель вкладакоў моментальна зміняюцца.

Є адны детал, які варта прыйняць: якщо значэнне контекста ў кожны раз, калі перерабляецца прадаўца, ёсьць новым об’ектам, то всі корыстувачы таксама перерабляюцца ў кожны раз, калі перерабляецца прадаўца. Зберагчы значэнне за дапамогою useMemo, ключаванае па тэме, можна ухіліцца ад гэтага. У такой маленькай прыкладнай програме гэта практычна не мае значэння, але гэта становіцца важлівым, калі багато компонентаў викорыстоўваюць контекст.

Зберагчы выбор за дапамогою AsyncStorage

Зараз можна выбраць тэму, але выбар згубляецца пасля перзагрузкі. Расшырьце функцыю setTheme так, каб, адгульніваючы стан, яна таксама зберагала выбар у AsyncStorage. Болей надзеяным варыянтом є зберагачыць key тэмы, а не цэлы об’ект: якщо пазней зменіць кольоры тэмы ў themes.json, корыстувачы будуць атрымляць адгульненую версію, а не застарэлы копію.

Вярненне тэмы пад запуск за дапамою useEffect

Пасляпэдній крок — це чытанне зберажанага тэмы калі запускаецца аплікэцыя. useEffect выконваецца пасля атрыбутавання компаненту дадзеных, таму ён падходы для выканання дадзеных эфектаў, такіх як чытанне дадзеных з памяці. У термінах класовых компанентаў гэта адпавядае таму, што раней выконвалі componentDidMount і componentDidUpdate; часамы наводзіцца паўпораўнанне з componentWillReceiveProps, але гэта вводзіць у заблуду, ўжо таму што эфекты выконваюцца пасля атрыбутавання, а не перад прыбыццю новых параметраў.

Унутрь ThemeContextProvider трэба дадаць эфект, які чытае ключ, зберажаны ў AsyncStorage, знаходзі супарадную тэму і вызывае функцыю для змены стану. Два аспекты дапамагаюць гэтаму працаваць правільна:

  • Пасуйце пусты ўжоў залежнасцяў. Эфект должен выконвацца аднаразова, калі запускаецца прадастальнік, а не пасля кожнага атрыбутавання. Справака API хуків адказвае на тое, як ўжоў залежнасцяў кантролюе час выконання эфекта.
  • Керуйце станам загрузкі. AsyncStorage ўжоў асінхронны, таму перша атрыбутавання выконваецца раней, чым стае вядомы зберажаны тэмат. Следзіце за тым, чыя загрузка завершылася, і не атрыбутавайце нічога (або паказвайце тымчасавы відгук) пакуль гэта не будзе зроблена. У працоўных случаях корыстувачы бачаць стандартны тэмат раней, чым пакажыцца іх выбраны тэмат.

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

Заключэнне

Готавы ўтварэнне — это адна функцыйная складовая, якая керуе станам тэмы, зберагае змяны, восстанавляе іх пад запуск і выдае ўсё через контэкст. Паўстаноўна да версіі на адной класе, логіка ў яй корачэй і чытаемая зверху да ніжней.

Калькуюцца калякі пункты на іншыя функцыі:

  • Зберагаюцься спяльныя настройкі UI у контэкстным прадастальніку поблізу корана, а таксама выдаецца функцыя для задання значэння.
  • Зберагаюцься ідэнтыфікаторы, а не цэлыя об’екты, тады змяны данных не заставляюць старых копій на прыстроях.
  • Асінхронныя запускавыя чытання спрацоўваюць як стан завантажэння, а не як выкарыстоўванне стандартных значэнняў з пазнейшым заменай.
  • Калі вы вже адаптаваліся да гэтых трох хуков, паглядзіце на useReducer для болейш абстрактных станоў, useRef для зменных значэнняў, якія не павінны спрычыніць перзапіс стану, і useLayoutEffect для задач, якія павінны выконвацца раней за адрасаванне элемента. Канвертаванне існуючага класовага компонента — гарны спосаб прыкладзіцца.
  • Спаднёе чытанне