Notes on React Native

Architecture Overview

The greatest injustice I've seen in software development is the unnecessary complexity around project structure. Organizing your files by domain (or feature), instead of by function, allows your code to mirror the UI/UX and act as a virtual map of features

── app (or src)
   ├── assets
   ├── global
   ├── libs
   ├── store
   ├── views
   └── index.js

For example, it's your first day at a new startup. Imagine you knew next to nothing about the project you're working on other than that you have users and they manage cooking recipes in the project. A manager asks you to "change the profile section to look like facebook". Which file structure would help you get started the fastest?

By Domain

── app (or src)
   ├── assets
   ├── global
   ├── libs
   ├── store
   │   ├── auth 
   │   ├── recipes 
   │   ├── settings 
   │   └── user 
   │       ├── actions.js 
   │       ├── reducer.js 
   │       └── state.js 
   ├── views
   │   ├── recipes 
   │   └── user 
   │       ├── profile
   │       │   └── index.js (*)
   │       └── index.js 
   └── index.js

By Function (Type)

── app (or src)
   ├── assets
   ├── styles
   ├── utils
   ├── components
   │   └── user 
   │       ├── UserComponent.js
   │       └── profile
   │           └── UserProfileComponent.js
   ├── pages
   │   └── user
   │       └── profile
   │           └── ProfilePage.js
   ├── routes
   │   └── user
   │       └── profile
   │           └── UserRoute.js
   ├── redux
   ├── index.android.js
   ├── index.ios.js 
   ...

While preference is subjective, I've onboarded new team members on projects with both, but I've had them deploying faster when structuring 'by domain'.

Offloading the cognitive map of the project to the file structure prevents developers from relying solely on UML or diagrams that structure the flow of the project. The file structure acts as a map to the UI and even the domain logic contained within. You may have observed that the by domain example seemingly has a top layer of by function structure. Viewing the project with higher architectural scope allows you to view the 'store' and 'views' as feature sets. Many projects do not have explicit UI/UX (CLI tools or API's) or use a 'store' for handling immutable state (potentially API's). Instead, they may be labeled as 'output' but still contain a 'store' to manage state.


UI Libraries

react-native-paper

  • Minimally styled components
  • Easy to override styling
  • Heavily opinionated styling
  • Absolute positioning on elements makes it hard to layout
  • Overriding styling is sometimes not possible
  • Bloated theming

Domain Logic

Helpful Commands

# android
emulator -list-avds
emulator -avd <id>

# ios
xcrun simctl delete unavailable
xcrun simctl list devices
xcrun simctl boot <id>

## record
xcrun simctl io booted recordVideo <filename>.<extension>

Other

Tips

Absolute Import Paths

module.exports = {
  presets: ['module:metro-react-native-babel-preset', 'module:react-native-dotenv'],
  plugins: [
    ['module-resolver', {
      alias: {
        app: './app',
      }
    }]
  ]
};

Tricks

Custom iOS Back

   <TouchableOpacity
    onPress={navigation.getParam('onNavigateBack')}
    style={{ flex: 1, flexDirection: 'row', alignItems: 'center' }}>
        <Icon style={{ color: navColors.appleBlue, paddingTop: 3, fontSize: 34 }} name='ios-arrow-back' />
        <Text style={{ color: navColors.appleBlue, paddingLeft: 4, fontSize: 18 }} >Back</Text>
    </TouchableOpacity>


Hidden UI Blocking Layer

    <TouchableWithoutFeedback onPress={this.onForceLinkAlert}>
            <View style={{
              position: 'absolute',
              elevation: 10,
              right: 0,
              top: 0,
              height: 75,
              width: 75,
              opacity: 0,
              backgroundColor: colors.primary,
              borderColor: colors.primary,
              borderWidth: 1,
              borderRadius: 50,
              zIndex: 5,
            }}/>
          </TouchableWithoutFeedback>


Diagonal Cut Linear Gradient

 const colorStart = '#FEFEFE'
 const colorMiddle = '#909090'
 const colorEnd = '#000000'

<LinearGradient
          style={[styles.card, {
            flexDirection: 'column',
            justifyContent: 'space-between',
            backgroundColor: colorStart,
          }]}
          colors={[colorStart, colorStart, colorMiddle, colorMiddle, colorEnd, colorEnd]}
          locations={[0, 0.45, 0.45, 0.75, 0.75, 1]}
          start={{ x: 0.00, y: 0.25 }}
          end={{ x: 0.035, y: 1 }}>

</LinearGradient>


Smooth iOS Status Bar Transitions

    // iOS Only - will switch back to light status bar on transition
    // Allows setting the status bar to the previous pages status bar color until
    // the end of the transition animation between screens
    blurListener = this.props.navigation.addListener(
      'willBlur',
      () => {
        setNavigatingHome(false);
      }
    );

    focusListener = this.props.navigation.addListener(
      'willFocus',
      () => {
        setNavigatingHome(true);
      }
    );