Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Mid Game
Search
Sponsored
·
SiteGround - Reliable hosting with speed, security, and support you can count on.
→
Radoslav Stankov
October 25, 2023
Technology
240
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
Building LLM Powered Features
rstankov
0
160
Tips for Tailwind CSS
rstankov
0
70
Building LLM Powered Features (lightning talk)
rstankov
0
81
All you need is CSS
rstankov
0
150
Ruby on Rails The Single Engineer Framework
rstankov
0
61
Rails: The Missing Parts
rstankov
1
270
The dream that turned into nightmare
rstankov
0
330
The dream that turned into nightmare (lightning)
rstankov
0
140
Ruby on Rails - The Single Engineer Framework
rstankov
0
370
Other Decks in Technology
See All in Technology
ボーイスカウトルールでメモリやスキルを改善しよう
azukiazusa1
4
1.5k
探索・可視化・自動化を一本化 Amazon Quickでデータ活用スピードを上げる方法
koheiyoshikawa
0
130
AI時代の開発生産性を捉え直す — 経営と現場をつなぐ「開発組織のオブザーバビリティ」— / AI Dev Ex Conference 2026
tkyowa
0
630
そのドキュメント、自動化しませんか?
yuksew
1
350
生成AI×AWS CDK×AWS FISで"振り返れる"ミニGameDayをつくろう
yoshimi0227
1
460
OPENLOGI Company Profile for engineer
hr01
1
74k
Vポイント分析基盤におけるデータモデリング20年史
taromatsui_cccmkhd
2
220
AIレビューはどこまで任せられるのか?自動化と人が背負うレビューの境界
sansantech
PRO
3
1.2k
「休む」重要さ
smt7174
1
160
第67回コンピュータビジョン勉強会CVPR2026読会前編
tsukamotokenji
0
160
変更し続けられるシステムをどう保つか — AI時代のSSoTという設計原則
kawauso
1
570
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
4
2.4k
Featured
See All Featured
The Art of Programming - Codeland 2020
erikaheidi
57
14k
SEO in 2025: How to Prepare for the Future of Search
ipullrank
3
3.6k
What does AI have to do with Human Rights?
axbom
PRO
1
2.3k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
220
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
310
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Leo the Paperboy
mayatellez
8
1.9k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
GraphQLとの向き合い方2022年版
quramy
50
15k
Fireside Chat
paigeccino
42
4k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
120k
The SEO Collaboration Effect
kristinabergwall1
1
510
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