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
150
Building LLM Powered Features
rstankov
0
190
Tips for Tailwind CSS
rstankov
0
87
Building LLM Powered Features (lightning talk)
rstankov
0
110
All you need is CSS
rstankov
0
180
Ruby on Rails The Single Engineer Framework
rstankov
0
70
Rails: The Missing Parts
rstankov
1
290
The dream that turned into nightmare
rstankov
0
360
The dream that turned into nightmare (lightning)
rstankov
0
170
Other Decks in Technology
See All in Technology
PagerDuty Tech Hub-鳴らすけれど、人は動かない
ezaki
0
190
NDIでApple Vision Proにリアルタイム映像送信
jyouryuusui
0
130
React改造してみた
tsukina_7mochi
0
140
ソフトウェア ファクトリー - 再現性のあるソフトウェアの工場づくり / Software factory
iktakahiro
7
5.2k
Browser Spec: Testing Hotwire with Ruby
takahashim
1
140
価値について
gree_tech
PRO
0
130
日本語に強いオープンな大規模言語モデル「LLM-jp」
odashi
5
1.2k
1つのリクエストを追って理解する - llm-dのP/D分離
akhino
1
180
hackers_champloo_2026_tidb
makocchi
0
170
Post AI時代のリファクタリング実践記 / Refactoring in the post AI era
kaminashi
1
310
Local CI × GitHub Actions
gogutan
3
230
導入から定着へ ~コーディングエージェントを日常業務に組み込む実践知~
yukisakai1225
0
110
Featured
See All Featured
Introduction to Domain-Driven Design and Collaborative software design
baasie
1
1k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
360
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
310
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2.5k
Why Your Marketing Sucks and What You Can Do About It - Sophie Logan
marketingsoph
0
430
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
2
2k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Google's AI Overviews - The New Search
badams
0
1.6k
How Software Deployment tools have changed in the past 20 years
geshan
2
35k
Ruling the World: When Life Gets Gamed
codingconduct
0
410
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
500
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