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
130
Building LLM Powered Features
rstankov
0
180
Tips for Tailwind CSS
rstankov
0
80
Building LLM Powered Features (lightning talk)
rstankov
0
94
All you need is CSS
rstankov
0
170
Ruby on Rails The Single Engineer Framework
rstankov
0
68
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
顧客に向き合う開発組織へ。リアーキテクチャとフィーチャーチーム化で挑む組織改革
safie
0
2.5k
AIは推し活である。
kurazuuuuuu
2
980
人間はどの意思決定を手放せるのか
kawasima
16
8.3k
あけおめLINE 傾向とその対策
nasa9084
0
330
AI 時代のスタートアップエコシステ厶から考究する技術的負債との向き合い方
m3m0r7
PRO
3
2.7k
Kiro Meetup #8 Kiro アップデート (2026/3/21〜2026/9/24)
katzueno
1
230
ADKで始める業務改善 - AIエージェント開発時の考えと設計
harappa80
2
290
SREは、MCPとAutopilotをこう使え!
kazumax55
3
970
負債のメタファと2026年 / Debt Metaphor in Agentic Engineering Age 202609 Edition
twada
PRO
13
7.4k
Claude Codeを「使うほど育つ」AI秘書にするノウハウ
minorun365
PRO
33
32k
JSONataとAWS Step Functionsで目指すRuntimelessな世界
mu7889yoon
1
500
Genieを崇めよ
kameitomohiro
0
170
Featured
See All Featured
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
KATA
mclloyd
PRO
35
16k
The Anti-SEO Checklist Checklist. Pubcon Cyber Week
ryanjones
0
250
How to build a perfect <img>
jonoalderson
1
6k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
290
Marketing to machines
jonoalderson
1
5.8k
Mind Mapping
helmedeiros
1
370
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
68
58k
Thoughts on Productivity
jonyablonski
76
5.4k
Claude Code のすすめ
schroneko
67
230k
Google's AI Overviews - The New Search
badams
0
1.6k
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