Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
React Mid Game
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Radoslav Stankov
October 25, 2023
Technology
250
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
React Mid Game
Radoslav Stankov
October 25, 2023
More Decks by Radoslav Stankov
See All by Radoslav Stankov
Tips for Building Useful Agent Skills
rstankov
0
140
Building LLM Powered Features
rstankov
0
180
Tips for Tailwind CSS
rstankov
0
81
Building LLM Powered Features (lightning talk)
rstankov
0
96
All you need is CSS
rstankov
0
180
Ruby on Rails The Single Engineer Framework
rstankov
0
69
Rails: The Missing Parts
rstankov
1
280
The dream that turned into nightmare
rstankov
0
350
The dream that turned into nightmare (lightning)
rstankov
0
160
Other Decks in Technology
See All in Technology
[2026 Oracle Technical Deep Dive] オンプレミスDBのCloud移行アプローチ:移行計画に基づくメソッドとツールの選択 (2026年9月17日開催)
oracle4engineer
PRO
0
110
AWS FinOps Agent 結局何が得意なの?
siromi
0
230
AI駆動開発、viviONの1年 ── うまくいったこと・いかなかったこと
vivion
0
140
1万名の社員が使う認証基盤で どう信頼性を担保するか?
kairim0
0
180
検証フェーズはAIの回答を判断するための学習機会
toru_kubota
2
380
Argo CDとAtlantisで実現するインフラ管理のセルフサービス化──小規模SREチームで支えるプラットフォーム
cassius7
0
280
Oracle Base Database Service 技術詳細
oracle4engineer
PRO
16
120k
Account Factory for Terraformによる 標準化されたアカウント発行の自動化
pensuke628
0
120
[2026 Oracle Technical Deep Dive] エンタープライズAIエージェントを支えるOCIソリューション。ラインナップと特徴を理解しよう! (2026年9月17日開催)
oracle4engineer
PRO
0
280
生成AIを使って「人が」考える技術 ― AI時代の人機共想と実践ノウハウ|UNITT AC2026
ishiirikie
0
520
HolmesGPTで始めるSREエージェント入門!プラットフォームの障害調査はAIにお任せ 〜
leveragestech
PRO
0
140
企業の現実世界をグラフで写し取る
sansantech
PRO
0
250
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
SEO for Brand Visibility & Recognition
aleyda
0
4.8k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
500
Making Projects Easy
brettharned
120
6.8k
Scaling GitHub
holman
464
140k
Building an army of robots
kneath
307
47k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
56
3.5k
From π to Pie charts
rasagy
1
380
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.7k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
1
620
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.4k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Transcript
React Native Midgame
!
Radoslav Stankov @rstankov rstankov.com
None
None
None
" Side project
None
- implement a mobile app # - one developer (me)
$ - support iOS % / Android & - ship it as fast as possible '
Going native (swift/kotlin) wasn't an option
None
None
None
None
( backed by Google ) uses Dart * cross platform
+ compiles to native machine code , custom components - battery included
None
. backed by Facebook / uses Javascript (or TypeScript) 0
uses React 1 cross platform 2 minimal 3 native and JS bridge 4 native components
React Native in 2019 was 5 React Native in 2021
was 6 React Native in 2022 was 7 React Native in 2023 still is 7
None
React Native is fast enough has a rich ecosystem Flutter
is faster has more batteries included I have done 3 React Native apps previously I know React/TypeScript/GraphQL extremely well I don't know Dart I don't know Flutter
source: https://refactoring.fm/p/how-to-choose-technology
None
None
None
Expo in 2020 was ... 8 Expo in 2020 was
... 9 Expo in 2022 was ... 7 Expo in 2023 is ...:
None
# Tech Stack
None
Architecture
None
None
None
0 Make common operations easy 4 Static type-safety 1 Isolate
dependencies + Extensibility and reusability
0 Make common operations easy 4 Static type-safety 1 Isolate
dependencies + Extensibility and reusability
2 1 3 Support Components Screens
Support Components Screens
1) Support 2) Components 3) Screens
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx 1 Support Components Pages 2 3
None
3 Screens
None
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
{ "expo": { "name": "Angry Building", "version": "1.48.0", "orientation": "portrait",
"icon": "./assets/icon.png", "splash": { "image": "./assets/splash.png", "resizeMode": "contain", "backgroundColor": "#252629" } }, // ... }
None
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
None
@react-navigation/native
None
None
... I still don't use it, because it was released
fairly recently ; ... however the way the app is setup, transition should be easy <
Overlay
Overlay Tab Navigation
Overlay Tab Navigation Stack Navigation Title
Overlay Tab Navigation Stack Navigation Title Screen
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
import screens from 'src/screens'; import { NavigationContainer } from '@react-navigation/native';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { createStackNavigator } from '@react-navigation/stack'; Sentry.init(); const RootStack = createStackNavigator(); export default function App() { return ( <ActionSheetProvider> <ApolloProvider client={apolloClient}> <NavigationContainer> <WithViewer> <RootStack.Navigator> <RootStack.Screen name="Main" component={TabsScreen} options={{ header <RootStack.Screen {...screens.apartmentNotOwning} /> <RootStack.Screen {...screens.apartmentOwning} /> <RootStack.Screen {...screens.apartmentUserCreate} /> <RootStack.Screen {...screens.issueCreate} /> <RootStack.Screen {...screens.serviceRequestCreate} /> <RootStack.Screen {...screens.services} /> </RootStack.Navigator> </WithViewer> </NavigationContainer> </ApolloProvider>
export default function App() { return ( <ActionSheetProvider> <ApolloProvider client={apolloClient}>
<NavigationContainer> <WithViewer> <RootStack.Navigator> <RootStack.Screen name="Main" component={TabsScreen} options={{ header <RootStack.Screen {...screens.apartmentNotOwning} /> <RootStack.Screen {...screens.apartmentOwning} /> <RootStack.Screen {...screens.apartmentUserCreate} /> <RootStack.Screen {...screens.issueCreate} /> <RootStack.Screen {...screens.serviceRequestCreate} /> <RootStack.Screen {...screens.services} /> </RootStack.Navigator> </WithViewer> </NavigationContainer> </ApolloProvider> </ActionSheetProvider> ); }
const Tab = createBottomTabNavigator(); function TabsScreen() { return ( <Tab.Navigator
screenOptions={tabNavigatorOptions}> <Tab.Screen name="buildingTab" component={Screen} initialParams={{ initialRoute: screens.home.name }} options={/* ... */} /> <Tab.Screen name="apartmentTab" component={Screen} initialParams={{ initialRoute: screens.apartment.name }} options={/* ... */} /> <Tab.Screen name="bulletinBoardTab" component={Screen} initialParams={{ initialRoute: screens.bulletinBoard.name }} options={/* ... */} /> <Tab.Screen name="cashReserveTab" component={Screen} initialParams={{ initialRoute: screens.cashReserve.name }}
/> <Tab.Screen name="apartmentTab" component={Screen} initialParams={{ initialRoute: screens.apartment.name }} options={/* ...
*/} /> <Tab.Screen name="bulletinBoardTab" component={Screen} initialParams={{ initialRoute: screens.bulletinBoard.name }} options={/* ... */} /> <Tab.Screen name="cashReserveTab" component={Screen} initialParams={{ initialRoute: screens.cashReserve.name }} options={/* ... */} /> <Tab.Screen name="issuesTab" component={Screen} initialParams={{ initialRoute: screens.issues.name }} options={/* ... */} /> </Tab.Navigator> ); }
const ScreenStack = createStackNavigator(); function Screen(props) { const initialRoute =
props.route.params.initialRoute; return ( <ScreenStack.Navigator initialRouteName={initialRoute}> <ScreenStack.Screen {...screens.apartmentHeatingMeasurements} /> <ScreenStack.Screen {...screens.apartmentNotes} /> <ScreenStack.Screen {...screens.apartmentPayments} /> <ScreenStack.Screen {...screens.apartmentTaxes} /> <ScreenStack.Screen {...screens.apartmentUnpaidTaxes} /> <ScreenStack.Screen {...screens.apartmentUsers} /> <ScreenStack.Screen {...screens.apartment} /> <ScreenStack.Screen {...screens.buildingNotes} /> <ScreenStack.Screen {...screens.bulletinBoardPostCreate} /> <ScreenStack.Screen {...screens.bulletinBoardPostUpdate} /> <ScreenStack.Screen {...screens.bulletinBoard} /> <ScreenStack.Screen {...screens.cashReserve} /> <ScreenStack.Screen {...screens.contacts} /> <ScreenStack.Screen {...screens.home} /> <ScreenStack.Screen {...screens.issues} /> <ScreenStack.Screen {...screens.messages} /> <ScreenStack.Screen {...screens.settings} /> <ScreenStack.Screen {...screens.votingPoll} /> </ScreenStack.Navigator> ); }
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
import apartment from './apartment'; import home from './home'; // ...
export default { apartment, home, // ... };
Screen States Layout
Loading State Screen States Layout
Loading State Error State Screen States Layout
Loading State Not Found Error Server Error Authorization Error Authentication
Error Error State Screen States Layout
Loading State Not Found Error Server Error Authorization Error Authentication
Error Error State Loaded State Screen States Layout
Loading State Not Found Error Server Error Authorization Error Authentication
Error Error State Loaded State render Screen States Layout
interface IOptions<D, P> { name: string; query?: DocumentNode; queryVariables?: object
| ((params: P) => object); queryRefreshOnShow?: boolean; component: IComponent<D, P>; type?: keyof typeof layouts; headerTitle?: ITranslation; background: IBackground; } interface IScreen { name: string; options: StackNavigationOptions; component: any; } export default function createScreen<D = any, P = any>(options: IOptions<D, P>): IScreen // ... }
screens/[name]/index.ts (createScreen) -> screens/index.ts -> app.ts
export default createScreen<IHomeScreen>({ name: 'home', type: 'plain', query: QUERY, background:
'black', component({ data, fetchMore, refetch }) { usePushNotificationsRegister(); usePushNotificationHandle(data.viewer); return ( <View style={styles.container}> <ImageBackground resizeMode="cover" source={image} style={styles.topBackgroun <View style={styles.bottomBackground} /> <BuildingApartmentsList refetch={refetch} fetchMore={fetchMore} building={data.building} /> <ReportIssueButton /> </View> ); }, });
▾ src/ ▾ screens/ ▾ home/ ▸ Status/ background.png BuildingApartmentsList.tsx
Header.tsx index.tsx Query.ts SelectEntrancePicker.tsx
http://graphql.org/
None
graphql-codegen --config codegen.yml
components/ApartmentStatus/Fragment.tsx import { gql } from '@apollo/client'; export default gql`
fragment IApartmentStatusFragment on Apartment { id number name overdueAmount } `;
types/grahpql.ts export type IApartmentStatusFragment = { __typename: 'Apartment'; id: string;
number?: string | null; name?: string | null; overdueAmount: number; };
import { IApartmentStatusFragment } from '~/types/graphql';
import { gql } from '@apollo/client'; import IHomeScreenStatusFragment from './Status/Fragment';
import IApartmentStatusFragment from 'src/components/ApartmentStatus/Fragment'; export default gql` query IHomeScreen($cursor: String) { building { id name apartments(first: 100, after: $cursor) { nodes { id name floor isViewerSelected ...IApartmentStatusFragment } pageInfo { hasNextPage endCursor } } ...IHomeScreenStatusFragment } } ${IHomeScreenStatusFragment} ${IApartmentStatusFragment} `; screens/home/Query.ts
Screen Component Component Component Component Component Component
Query Fragment Fragment Fragment Fragment Fragment Fragment
None
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
import routes from '~/routes'; routes.back; routes.home; routes.bulletinBoardPostUpdate(post);
<Button to={routes.home} />
import { useNavigation } from '@react-navigation/native'; import screens from 'src/screens';
type IScreenName = keyof typeof screens; export type IRoute = | { transition: 'goBack' } | { screen: IScreenName; params?: IParams; root?: boolean } | { screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, };
import { useNavigation } from '@react-navigation/native'; import screens from 'src/screens';
type IScreenName = keyof typeof screens; export type IRoute = | { transition: 'goBack' } | { screen: IScreenName; params?: IParams; root?: boolean } | { screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, };
import { useNavigation } from '@react-navigation/native'; import screens from 'src/screens';
type IScreenName = keyof typeof screens; export type IRoute = | { transition: 'goBack' } | { screen: IScreenName; params?: IParams; root?: boolean } | { screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, };
import { useNavigation } from '@react-navigation/native'; import screens from 'src/screens';
type IScreenName = keyof typeof screens; export type IRoute = | { transition: 'goBack' } | { screen: IScreenName; params?: IParams; root?: boolean } | { screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, };
| { screen: IScreenName; params?: IParams; root?: boolean } |
{ screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, }; export default routes; export interface INavigation { navigate: (screen: string, params?: IParams) => void; goBack: VoidFunction; }
| { screen: IScreenName; params?: IParams; root?: boolean } |
{ screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, }; export default routes; export interface INavigation { navigate: (screen: string, params?: IParams) => void; goBack: VoidFunction; }
import { useNavigation } from '@react-navigation/native'; import screens from 'src/screens';
type IScreenName = keyof typeof screens; export type IRoute = | { transition: 'goBack' } | { screen: IScreenName; params?: IParams; root?: boolean } | { screen: 'buildingTab' | 'apartmentTab' | 'cashReserveTab' | 'issuesTab'; params?: IParams; root: true; }; const routes = { back: { transition: 'goBack' } as IRoute, home: { root: true, screen: 'buildingTab' } as IRoute, // ... bulletinBoardPostUpdate(post: { id: string }) { return { screen: 'bulletinBoardPostUpdate', params: { post }, } as IRoute; }, };
export interface INavigation { navigate: (screen: string, params?: IParams) =>
void; goBack: VoidFunction; } export function navigate(navigation: INavigation, to: IRoute) { if ('transition' in to) { navigation.goBack(); } else if (to.root) { navigation.navigate('Main', to); } else { navigation.navigate(to.screen, to.params); } } export function useNavigate() { const navigation: any = useNavigation(); const fn = React.useCallback( (to: IRoute) => { navigate(navigation, to); }, [navigation], ); return fn; }
import routes, { useNavigate } from '~/routes'; export function MyComponent()
{ const navigate = useNavigate(); const onPress = () => { navigate(routes.home); } return ( <TouchableOpacity onPress={onPress}> <Text>Visit product</Text> </TouchableOpacity> ); }
2 Components
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
None
If component is used in more than 1 screens then
goes to src/components
= generic components > domain components
[Domain][Name][Type]
? Component as directory components/ PublicComponent/ PrivateSubComponent/ Fragment.ts Mutation.ts index.ts
styles.ts utils.ts
? Component as directory components/ PublicComponent/ PrivateSubComponent/ Fragment.ts Mutation.ts index.ts
styles.ts utils.ts
<Text bold={true} size="l" color="black">Text</Text>
<Button to={routes.profile(profile)} />
<Button to={routes.profile(profile)} /> <Button onPress={onClickReturnsPromise} />
<Button to={routes.profile(profile)} /> <Button onPress={onClickReturnsPromise} /> <Button onPress={onClickReturnsPromise} confirm="Are you
sure?" requireLogin={true} />
<Button to={routes.profile(profile)} /> <Button onPress={onClickReturnsPromise} /> <Button onPress={onClickReturnsPromise} confirm="Are you
sure?" requireLogin={true} /> <Button mutation={MUTATION} input={input} onMutate={onMutate} />
<Button.Text {...props} /> <Button.Icon {...props} /> <Button.Solid {...props} /> <Button.Outline
{...props} />
None
<Flex.Row>{...}</Flex.Row> <Flex.Column>{...}</Flex.Column>
<Flex.Row gap="m">{...}</Flex.Row> <Flex.Column gap="m">{...}</Flex.Column>
1 Support
None
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
import { format } from 'date-fns'; export function formatDateTime(date: string)
{ return format(date, 'H:mm A · MMM D, YYYY'); } utils/date.ts
moment date.ts Component Page
date.ts Component Page date-fns
utils/ external/ Intercom/ OneSignal/ Segment/ Sentry/
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
useGraphQLFragment() useViewier() useIsLoggedIn()
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
@ A B
i18next
▾ src/ ▾ translations/ bg.json en.json es.json index.ts
import i18n from 'i18next'; import captureError from 'src/utils/captureError'; import {
isProduction } from 'src/config'; import bg from './bg.json'; import en from './en.json'; import es from './es.json'; // NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation: bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`);
import i18n from 'i18next'; import captureError from 'src/utils/captureError'; import {
isProduction } from 'src/config'; import bg from './bg.json'; import en from './en.json'; import es from './es.json'; // NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation: bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`);
import i18n from 'i18next'; import captureError from 'src/utils/captureError'; import {
isProduction } from 'src/config'; import bg from './bg.json'; import en from './en.json'; import es from './es.json'; // NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation: bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`);
// NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation:
bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`); return key; } throw new Error(`Key not found t('${key}')`); }, compatibilityJSON: 'v3', });
// NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation:
bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`); return key; } throw new Error(`Key not found t('${key}')`); }, compatibilityJSON: 'v3', });
// NOTE(rstankov): Documentation https://www.i18next.com/overview/configuration-optio i18n.init({ resources: { bg: { translation:
bg, }, en: { translation: en, }, es: { translation: es, }, }, lng: 'bg', saveMissing: true, missingKeyHandler: (_lngs: any, _ns: string, key: string, _fallbackValue: any) => { if (isProduction) { captureError(`Key not found t('${key}')`); return key; } throw new Error(`Key not found t('${key}')`); }, compatibilityJSON: 'v3', });
captureError(`Key not found t('${key}')`); return key; } throw new Error(`Key
not found t('${key}')`); }, compatibilityJSON: 'v3', }); export type ITranslation = keyof typeof bg; export type ILanguage = 'bg' | 'en' | 'es'; export default i18n.t as (key: keyof typeof bg, interpolations?: any) => string; export function changeLanguage(newLanguage: ILanguage) { if (i18n.language === newLanguage) { return; } return i18n.changeLanguage(newLanguage); }
export type ITranslation = keyof typeof bg; export type ILanguage
= 'bg' | 'en' | 'es'; export default i18n.t as (key: keyof typeof bg, interpolations?: any) => string; export function changeLanguage(newLanguage: ILanguage) { if (i18n.language === newLanguage) { return; } return i18n.changeLanguage(newLanguage); }
▾ src/ ▸ components/ ▸ hooks/ ▸ screens/ ▸ styles/
▸ translations/ ▸ types/ ▸ utils/ config.ts routes.ts app.json App.tsx
import * as React from 'react'; import { View, StyleSheet
} from 'react-native'; import s from 'src/styles'; import Logo from 'src/components/Logo'; export default React.memo(function Ribbon() { return ( <> <View style={styles.topBackground}> <Logo height={127} /> </View> <View style={styles.container}> <View style={styles.view} /> </View> </> ); }); const styles = StyleSheet.create({ container: { backgroundColor: s.colors.black, }, view: { marginTop: 10, borderTopLeftRadius: s.radius, borderTopRightRadius: s.radius, width: '100%',
import * as React from 'react'; import { View, StyleSheet
} from 'react-native'; import s from 'src/styles'; import Logo from 'src/components/Logo'; export default React.memo(function Ribbon() { return ( <> <View style={styles.topBackground}> <Logo height={127} /> </View> <View style={styles.container}> <View style={styles.view} /> </View> </> ); }); const styles = StyleSheet.create({ container: { backgroundColor: s.colors.black, }, view: { marginTop: 10, borderTopLeftRadius: s.radius, borderTopRightRadius: s.radius, width: '100%',
import * as React from 'react'; import { View, StyleSheet
} from 'react-native'; import s from 'src/styles'; import Logo from 'src/components/Logo'; export default React.memo(function Ribbon() { return ( <> <View style={styles.topBackground}> <Logo height={127} /> </View> <View style={styles.container}> <View style={styles.view} /> </View> </> ); }); const styles = StyleSheet.create({ container: { backgroundColor: s.colors.black, }, view: { marginTop: 10, borderTopLeftRadius: s.radius, borderTopRightRadius: s.radius, width: '100%',
}); const styles = StyleSheet.create({ container: { backgroundColor: s.colors.black, },
view: { marginTop: 10, borderTopLeftRadius: s.radius, borderTopRightRadius: s.radius, width: '100%', height: s.radius * 2, marginBottom: -s.radius, backgroundColor: s.colors.white, }, topBackground: { position: 'absolute', top: -500, height: 500, right: 0, left: 0, backgroundColor: s.colors.black, alignItems: 'center', justifyContent: 'flex-end', paddingBottom: s.spacing.l, }, });
const styles = StyleSheet.create({ container: { backgroundColor: s.colors.black, }, view:
{ marginTop: 10, borderTopLeftRadius: s.radius, borderTopRightRadius: s.radius, width: '100%', height: s.radius * 2, marginBottom: -s.radius, backgroundColor: s.colors.white, }, topBackground: { position: 'absolute', top: -500, height: 500, right: 0, left: 0, backgroundColor: s.colors.black, alignItems: 'center', justifyContent: 'flex-end', paddingBottom: s.spacing.l, }, });
<Flex marginLeft="m" /> <Button marginLeft="m" /> <Text marginLeft="m" />
<Flex paddingLeft="l" /> <Button paddingLeft="l" /> <Text paddingLeft="l" />
import variables from 'src/styles'; export type ISpacing = keyof typeof
spacing; export interface ISpacingProps { marginBottom?: ISpacing | null; marginHorizontal?: ISpacing | null; marginLeft?: ISpacing | null; marginRight?: ISpacing | null; marginTop?: ISpacing | null; marginVertical?: ISpacing | null; margin?: ISpacing | null; padding?: ISpacing | null; paddingBottom?: ISpacing | null; paddingHorizontal?: ISpacing | null; paddingLeft?: ISpacing | null; paddingRight?: ISpacing | null; paddingTop?: ISpacing | null; paddingVertical?: ISpacing | null; } export function spacingStyle(props: ISpacingProps, style: any = {}) { if (props.marginTop) { style.marginTop = variables.spacing[props.marginTop]; } // ... return style; }
import { spacingStyle, ISpacing } from '~/styles/spacing'; interface IProps extends
ISpacingProps { someProps: any, // ... } function MyComponent(props) { // ... return ( <View style={spacingStyle(props)}> {/* ... */} </View> ); }
None