React Native

Fundamentals

Native Components

Text Input

Using a ScrollView

Using List Views

Design

Layout

StyleSheet

const App = () => (
  <View style={styles.container}>
    <Text style={styles.title}>React Native</Text>
  </View>
);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 24,
    backgroundColor: "#eaeaea"
  },
  title: {
    marginTop: 16,
    paddingVertical: 8,
    borderWidth: 4,
    borderColor: "#20232a",
    borderRadius: 6,
    backgroundColor: "#61dafb",
    color: "#20232a",
    textAlign: "center",
    fontSize: 30,
    fontWeight: "bold"
  }
});
const styles1 = StyleSheet.create({
    button: {
        backgroundColor: 'blue',
        color: 'white'
    }
})

const styles2 = StyleSheet.create({
    button: {
        color: 'yellow'
    }
})

const composedStyles = StyleSheet.compose(styles1, styles2)
// creates the following:
{
    button: {
        backgroundColor: 'blue',
        color: 'yellow',
    }
}
const styles1 = StyleSheet.create({
    button: {
        backgroundColor: 'blue',
        color: 'white'
    }
})

const styles2 = StyleSheet.create({
    button: {
        color: 'yellow'
    }
})

const flattenedStyle = StyleSheet.flatten([styles1.button, styles2.button])
// creates the following:
{
    backgroundColor: 'blue',
    color: 'yellow',
}
<View style={[styles.background, StyleSheet.absoluteFill]}>Hello World</View>
const styles = StyleSheet.create({
    background: {
        ...StyleSheet.absoluteFillObject,
        backgroundColor: 'deeppink',
    }
})
const styles = StyleSheet.create({
    listItem: {
        borderBottomColor: 'gray',
        borderBottomWidth: StyleSheet.hairlineWidth,
    }
})

Images

// GOOD
<Image source={require('./my-icon.png')} />;

// BAD
var icon = this.props.active
  ? 'my-icon-active'
  : 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;

// GOOD
var icon = this.props.active
  ? require('./my-icon-active.png')
  : require('./my-icon-inactive.png');
<Image source={icon} />;

Colors

const styles = StyleSheet.create({
  label: {
    padding: 16,
    ...Platform.select({
      ios: {
        color: PlatformColor('label'),
        backgroundColor:
          PlatformColor('systemTealColor'),
      },
      android: {
        color: PlatformColor('?android:attr/textColor'),
        backgroundColor:
          PlatformColor('@android:color/holo_blue_bright'),
      },
      default: { color: 'black' }
    })
  },

Networking

Touch (buttons)

Navigation

react-navigation

// In App.js in a new project

import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';

function HomeScreen() {
  return (
    <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
      <Text>Home Screen</Text>
    </View>
  );
}

const Stack = createNativeStackNavigator();

function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator initialRouteName="Home">
        <Stack.Screen name="Home" component={HomeScreen} options={{ title: 'Overview' }} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

export default App;

Safe areas

Platform-specific code

import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  height: Platform.OS === 'ios' ? 200 : 100
});
import { Platform, StyleSheet } from 'react-native';

const styles = StyleSheet.create({
  container: {
    flex: 1,
    ...Platform.select({
      ios: {
        backgroundColor: 'red'
      },
      android: {
        backgroundColor: 'green'
      },
      default: {
        // other platforms, web for example
        backgroundColor: 'blue'
      }
    })
  }
});

/* can also be used to return platform-specific components */

const Component = Platform.select({
  ios: () => require('ComponentIOS'),
  android: () => require('ComponentAndroid')
})();

<Component />;
// imports BigButton.ios.js or BigButton.android.js depending on platform
import BigButton from './BigButton';

React Native Web

Warning

Default browser styles for HTML elements are not applied!

<Text role="heading">All Articles</Text>

<View role="article">
    <Text role="heading" aria-level={2}>Article Title</Text>
    <Text role="paragraph">Article text goes here.</Text>
    <Text href="https://react.dev/">Link to React</Text>
</View>
<h1>All Articles</h1>

<article>
    <h2>Article Title</h2>
    <p>Article text goes here.</p>
    <a href="https://react.dev/">Link to React</a>
</article>

Expo

Create a new project:

npx create-expo-app@latest

module.exports = {
  extends: ['expo', 'prettier'],
  plugins: ['prettier'],
  rules: {
    'prettier/prettier': 'error',
  },
};

Basics

Routing & navigation

import { useLocalSearchParams } from 'expo-router'

export default function Page() {
    const { category } = useLocalSearchParams()
}

Page options

import { useNavigation } from 'expo-router'

const navigation = useNavigation()

useEffect(() => {
    navigation.setOptions({ headerShown: false })
}, [navigation])

Data storage

npx expo install expo-secure-store
 import * as SecureStore from 'expo-secure-store';
 
 await SecureStore.setItemAsync(key, value);
 const result = await SecureStore.getItemAsync(key);
npx expo install @react-native-async-storage/async-storage
import AsyncStorage from '@react-native-async-storage/async-storage';

await AsyncStorage.setItem('todos', JSON.stringify({ name: 'Buy eggs', complete: false }))

const todos = await JSON.parse(AsyncStorage.getItem('todos') ?? '{}')
npx expo install expo-file-system
npx expo install expo-sqlite

Tamagui

Fix "problem connecting to the react-native-css-interop Metro server"

import { Platform } from "react-native";

if (Platform.OS === "web") {
  import("../tamagui-web.css");
}

See also