Slide 1

Slide 1 text

Designing and maintaining an open-source React Native library React & React Native Bordeaux

Slide 2

Slide 2 text

Mathieu Acthernoene Lead front-end developer @ Swan Podcast host @ Putain de Code !

Slide 3

Slide 3 text

Maintainer of: react-native-bootsplash, react-native-localize, react-native-permissions

Slide 4

Slide 4 text

React Native isn't magical πŸ§™

Slide 5

Slide 5 text

React Native isn't magical πŸ§™ …The cost of cross-platform in react-native-localize

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

What's the bridge for? Reading sync data using constants, Calling async methods using promises or callbacks, Receiving events using event listeners

Slide 8

Slide 8 text

What's the bridge for? Reading sync data using constants, Calling async methods using promises or callbacks, Receiving events using event listeners

Slide 9

Slide 9 text

- (NSDictionary *)constantsToExport { return @{ @"initialTimeZone": [[NSTimeZone localTimeZone] name], }; } Objective-C TypeScript import { NativeModules } from "react-native"; const NativeModule: Readonly<{ initialTimeZone: string; }> = NativeModules.RNModuleTemplate; export const initialTimeZone = NativeModule.initialTimeZone;

Slide 10

Slide 10 text

@Override public @Nullable Map getConstants() { HashMap constants = new HashMap<>(); constants.put("initialTimeZone", TimeZone.getDefault().getID()); return constants; } Java TypeScript import { NativeModules } from "react-native"; const NativeModule: Readonly<{ initialTimeZone: string; }> = NativeModules.RNModuleTemplate; export const initialTimeZone = NativeModule.initialTimeZone;

Slide 11

Slide 11 text

What's the bridge for? Reading sync data using constants, Calling async methods using promises or callbacks, Receiving events using event listeners

Slide 12

Slide 12 text

RCT_EXPORT_METHOD( getTimeZone:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject ) { @try { resolve([[NSTimeZone localTimeZone] name]); } @catch (NSException *exception) { reject(@"error_code", exception.reason, nil); } } import { NativeModules } from "react-native"; const NativeModule: Readonly<{ getTimeZone: () = > Promise; }> = NativeModules.RNModuleTemplate; export function getTimeZone(): Promise { return NativeModule.getTimeZone(); } Objective-C TypeScript

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

@ReactMethod public void getTimeZone(Promise promise) { try { promise.resolve(TimeZone.getDefault().getID()); } catch (Exception exception) { promise.reject("error_code", exception.getMessage()); } } Java import { NativeModules } from "react-native"; const NativeModule: Readonly<{ getTimeZone: () = > Promise; }> = NativeModules.RNModuleTemplate; export function getTimeZone(): Promise { return NativeModule.getTimeZone(); } TypeScript

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

What's the bridge for? Reading sync data using constants, Calling async methods using promises or callbacks, Receiving events using event listeners

Slide 17

Slide 17 text

- (void)startObserving { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onTimeZoneChange) name:NSSystemTimeZoneDidChangeNotification object:nil]; } - (void)stopObserving { [[NSNotificationCenter defaultCenter] removeObserver:self name:NSSystemTimeZoneDidChangeNotification object:nil]; } - (NSArray *)supportedEvents { return @[@"timeZoneChange"]; } - (void)onTimeZoneChange { [self sendEventWithName:@"timeZoneChange" body:@{ @"date": [[NSISO8601DateFormatter new] stringFromDate:[NSDate date]], @"value": [[NSTimeZone localTimeZone] name], }]; } Objective-C import { NativeEventEmitter, NativeModules } from "react-native"; const emitter = new NativeEventEmitter(NativeModules.RNModuleTemplate); type Listeners = { timeZoneChange: (body: { date: string; value: string }) = > void; }; export function addListener( type: Type, listener: Listeners[Type], ): () = > void { const subscription = emitter.addListener(type, listener); return subscription.remove; } TypeScript

Slide 18

Slide 18 text

WritableMap body = Arguments.createMap(); DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.getDefault()); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); body.putString("date", dateFormat.format(new Date())); body.putString("value", TimeZone.getDefault().getID()); getReactApplicationContext() .getJSModule(RCTDeviceEventEmitter.class) .emit("timeZoneChange", body); import { NativeEventEmitter, NativeModules } from "react-native"; const emitter = new NativeEventEmitter(NativeModules.RNModuleTemplate); type Listeners = { timeZoneChange: (body: { date: string; value: string }) = > void; }; export function addListener( type: Type, listener: Listeners[Type], ): () = > void { const subscription = emitter.addListener(type, listener); return subscription.remove; } Java TypeScript

Slide 19

Slide 19 text

How to organize your module repository

Slide 20

Slide 20 text

Sources github.com/zoontek/react-native-module-template - or - github.com/callstack/react-native-builder-bob

Slide 21

Slide 21 text

PUBLISH

Slide 22

Slide 22 text

That was the first 20% of the work

Slide 23

Slide 23 text

No content

Slide 24

Slide 24 text

No content

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

( This one is a joke) ( It still triggered me)

Slide 27

Slide 27 text

Huge projects depend on single-maintainer dependencies

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

Preserve yourself

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

My tricks to avoid open-source burnout 😌

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

How it was done before github issue forms

Slide 38

Slide 38 text

Don't feel bad for not dealing with everything

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

No content

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

A friend πŸ‘‰ Also a friend πŸ‘‰ Also a friend πŸ‘‰

Slide 43

Slide 43 text

Don't do it for money πŸ’Έ

Slide 44

Slide 44 text

Know what you want to maintain Why not a PHILOSOPHY ( .md)? You think it belongs to module core You think it doesn't Consider the PR Feel free to refuse (and explain nicely why)

Slide 45

Slide 45 text

Know what you want to maintain Especially in the React Native environment

Slide 46

Slide 46 text

Think about: React Native updates and breaking changes iOS updates and breaking changes Android updates and breaking changes macOS updates and breaking changes Windows updates and breaking changes RNW updates and breaking changes React updates and breaking changes …

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Focus on one thing Let people create their own abstractions in user-land

Slide 50

Slide 50 text

Embrace help 🀝 Even if they don't know the tech, people can help you (triage, linking to similar issues, asking for more details, etc.)

Slide 51

Slide 51 text

πŸ™‡ Thank you! Questions? Mathieu Acthernoene Lead front-end developer @ Swan Podcast host @ Putain de Code !

Slide 52

Slide 52 text

Sources: - https://formidable.com/blog/2019/react-codegen-part-1 - https://mikemcquaid.com/2018/03/19/open-source-maintainers-owe-you-nothing/ - https://github.com/facebook/react-native/issues/23313 - https://github.com/doczjs/docz/issues/1634