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
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
92
Building LLM Powered Features
rstankov
0
180
Tips for Tailwind CSS
rstankov
0
76
Building LLM Powered Features (lightning talk)
rstankov
0
91
All you need is CSS
rstankov
0
170
Ruby on Rails The Single Engineer Framework
rstankov
0
66
Rails: The Missing Parts
rstankov
1
280
The dream that turned into nightmare
rstankov
0
340
The dream that turned into nightmare (lightning)
rstankov
0
150
Other Decks in Technology
See All in Technology
Multica × 長期記憶:40個のミニプロジェクト管理
eiei114
1
250
2026_devsumi_ozono.pdf
o3
2
400
[2026-09-11]SREは誰のもの?運用エンジニアが始める 「SRE領域への越境」とチームの進化の軌跡 〜Road to NEXT CRE
tosite
0
150
module Synths; end
asonas
1
130
薬剤師(ドメインエキスパート)と一緒に育てる薬局向けAIアシスタント
kakehashi
PRO
2
150
NW運用でNWトポロジ可視化ツールに期待すること/shumoku-meetup1
corestate55
2
110
20260912_スクフェス三河
kgnkhkr
0
300
全員がプロダクトへ向き合う組織を持続成長させるために——組織づくりのフライホイールと4象限 / The Flywheel Model and Four Quadrants for Organizational Design
hiro_torii
4
980
From Vanilla Kubernetes to a Batteries-Included Platform: Developer Experience at 1,300+ Clusters
yosshi_
0
680
深夜のクラウド懺悔室 1:29:300 or 1:0:0
kazzpapa3
1
230
Deploying a Full-Stack Bun-Native Framework on Cloudflare Workers
7nohe
0
140
GoCon2026 - Open Source, Open World
sanposhiho
4
3.2k
Featured
See All Featured
Measuring & Analyzing Core Web Vitals
bluesmoon
9
990
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
520
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Collaborative Software Design: How to facilitate domain modelling decisions
baasie
1
320
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.6k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.4k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
520
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
330
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
380
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
2
420
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