React Native is a popular open-source framework developed by Facebook for building mobile applications using JavaScript and React. It enables developers to create native apps for both iOS and Android platforms from a single codebase.
Before diving into coding, it's crucial to understand some basic concepts that form the foundation of React Native.
To start building applications with React Native, you need to set up your development environment and begin writing code.
npx react-native init MyApp
to create a new React Native project. This command sets up the initial project structure.npx react-native run-android
or npx react-native run-ios
to launch your app on the Android emulator or iOS simulator.Learning through examples is an effective way to understand how React Native works. Here are a few common examples:
Text
component.View
components with styles defined in StyleSheet.create()
to create structured layouts.TextInput
and button components to capture user data.React Native offers a rich set of built-in UI components that can be used to build your application's user interface.
View
, Text
, Image
, ScrollView
, and TouchableOpacity
.The official React Native documentation is a valuable resource for developers at all levels. It provides comprehensive information on the framework, including:
Node.js is a JavaScript runtime that is essential for React Native development. It allows you to run JavaScript on your machine and manage dependencies using npm (Node Package Manager).
node -v
and npm -v
to verify that Node.js and npm are installed successfully. You should see the version numbers printed in the console.Before starting with React Native, it's important to have a solid understanding of JavaScript, as React Native is built on it.
The React Native CLI (Command Line Interface) is a tool that helps you create and manage React Native projects easily.
npm install -g react-native-cli
react-native --version
to check if the CLI was installed correctly. You should see the version number displayed.Once your environment is set up, you can create a new React Native project using the CLI.
npx react-native init MyApp
cd MyApp
npx react-native run-android
or for iOS:
npx react-native run-ios
After creating a new project, it's essential to understand its structure to navigate and develop efficiently.
Now that you have set up your environment and created a new project, it's time to build your first React Native app!
App.js
file in your code editor and modify the default content to display a simple message:import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const App = () => {
return (
Hello, React Native!
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default App;
Components are the building blocks of a React Native application. They are reusable pieces of code that define how a certain part of the UI should appear and behave. Props (short for properties) are used to pass data from parent components to child components.
const Greeting = (props) => {
return Hello, {props.name}! ;
};
React.Component
. Example:class Greeting extends React.Component {
render() {
return Hello, {this.props.name}! ;
}
};
<Greeting name="John" />
State is a built-in object that stores component-specific data that can change over time. The component re-renders whenever the state changes. Lifecycle methods allow you to hook into different points in a component's lifecycle.
this.state
in class components and useState
in functional components:class Counter extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
}
}
const Counter = () => {
const [count, setCount] = useState(0);
};
setState
in class components or the state updater function in functional components:this.setState({ count: this.state.count + 1 });
setCount(count + 1);
componentDidMount()
: Invoked immediately after a component is mounted.componentDidUpdate()
: Invoked immediately after updating occurs.componentWillUnmount()
: Invoked immediately before a component is unmounted.Handling text input allows users to interact with your app by entering text data.
TextInput
component to create text fields:<TextInput
placeholder="Type here..."
onChangeText={text => this.setState({ inputText: text })}
value={this.state.inputText}
/>
placeholder
: Text displayed when the input is empty.value
: Controlled input value.onChangeText
: Callback that is called when the text changes.React Native provides various components to handle touch events, allowing users to interact with your app.
TouchableOpacity
: Adjusts opacity on press.TouchableHighlight
: Darkens background on press.TouchableWithoutFeedback
: No visual feedback on press.onPress
to handle touch events:<TouchableOpacity onPress={() => alert('Button Pressed!')} >
<Text>Press Me!</Text>
</TouchableOpacity>
The ScrollView
component allows users to scroll through a set of components that exceed the screen size.
ScrollView
:<ScrollView>
<Text>Item 1</Text>
<Text>Item 2</Text>
<Text>Item 3</Text>
...
</ScrollView>
horizontal
: Set to true for horizontal scrolling.onScroll
: Callback that is called when the scroll position changes.scrollEnabled
: Disable/enable scrolling.The FlatList
component is an efficient way to render large lists of data in React Native.
FlatList
to render a list of items:<FlatList
data={this.state.data}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={item => item.id}
/>
data
: Array of data to be rendered.renderItem
: Function that takes an item from the data and renders it.keyExtractor
: Function to extract a unique key for each item.Flexbox is a powerful layout model that allows for responsive design. It provides a way to arrange elements in a one-dimensional layout (row or column) with flexibility.
flexDirection
, justifyContent
, and alignItems
properties.const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row', // or 'column'
justifyContent: 'center', // Aligns children along the main axis
alignItems: 'center', // Aligns children along the cross axis
},
});
flex
: Defines how a component grows relative to its siblings.flexGrow
: Defines the ability for a component to grow.flexShrink
: Defines the ability for a component to shrink.flexBasis
: Defines the initial size of a component before space distribution.Animations enhance user experience by providing feedback and visual appeal. React Native provides the Animated
API for creating complex animations.
const fadeAnim = useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
Animated.timing(fadeAnim, {
toValue: 1,
duration: 2000,
useNativeDriver: true,
}).start();
<Animated.View style={{ opacity: fadeAnim }}>
<Text>Welcome to React Native!</Text>
</Animated.View>
Animated.sequence
, Animated.parallel
, and Animated.stagger
for more complex animations.Images are a key part of UI design in React Native. The Image
component allows for displaying static and dynamic images.
Image
component:<Image
source={{ uri: 'https://example.com/image.jpg' }}
style={{ width: 100, height: 100 }}
/>
<Image
source={require('./path/to/image.png')}
style={{ width: 100, height: 100 }}
/>
resizeMode
: Defines how the image should be resized to fit its container (e.g., cover
, contain
).onLoad
: Callback when the image has finished loading.onError
: Callback for handling image load errors.React Native makes it easy to fetch data from APIs using the fetch
API, which is built into JavaScript.
fetch
function to make network requests:fetch('https://api.example.com/data')
.then((response) => response.json())
.then((json) => {
// Handle the data
this.setState({ data: json });
})
.catch((error) => {
console.error(error);
});
<FlatList
data={this.state.data}
renderItem={({ item }) => <Text>{item.title}</Text>}
keyExtractor={item => item.id}
/>
Push notifications are a great way to keep users engaged. React Native provides libraries like react-native-push-notification
to handle notifications.
npm install --save react-native-push-notification
PushNotification.configure({
onNotification: function(notification) {
console.log("NOTIFICATION:", notification);
},
});
PushNotification.localNotification({
title: "My Notification Title",
message: "My Notification Message",
});
Debugging is an essential skill in development. React Native provides several tools and methods for debugging your application.
console.log()
to output messages to the console for debugging purposes.Cmd + M
on Android Emulator) and selecting Debug
.React Native Performance
or Flipper
for performance profiling and debugging.The React Navigation library is a popular solution for implementing navigation in React Native applications. It provides a flexible and customizable way to manage the navigation stack and handle transitions between screens.
npm install @react-navigation/native
npm install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view
NavigationContainer
to manage navigation state:import { NavigationContainer } from '@react-navigation/native';
const App = () => (
<NavigationContainer>
// Navigation stacks go here
</NavigationContainer>
);
Stack Navigation allows you to navigate between screens in a stack-like manner, where each new screen is pushed onto the stack, and you can pop back to the previous screen.
createStackNavigator
function to define your stack:import { createStackNavigator } from '@react-navigation/stack';
const Stack = createStackNavigator();
const MyStack = () => (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
navigation
prop to navigate between screens:const HomeScreen = ({ navigation }) => (
<View>
<Text>Home Screen</Text>
<Button
title="Go to Details"
onPress={() => navigation.navigate('Details')}
/>
</View>
);
Tab Navigation allows users to switch between different screens using tabs at the bottom or top of the screen.
createBottomTabNavigator
or createMaterialTopTabNavigator
to create tabs:import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const MyTabs = () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
screenOptions
prop:<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarIcon: ({ color }) => <Icon name="home" color={color} />,
}}
/>
Drawer Navigation provides a sidebar menu that slides in from the side, allowing users to navigate to different sections of the app.
createDrawerNavigator
function to create a drawer:import { createDrawerNavigator } from '@react-navigation/drawer';
const Drawer = createDrawerNavigator();
const MyDrawer = () => (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Profile" component={ProfileScreen} />
</Drawer.Navigator>
);
drawerContentOptions
to customize the drawer’s appearance:<Drawer.Navigator
drawerContentOptions={{
activeTintColor: 'blue',
itemStyle: { marginVertical: 5 },
}}
>
Switch Navigation allows for conditional navigation between different screens without a stack. It is often used for authentication flows.
createSwitchNavigator
(note that it's part of React Navigation 5+ under @react-navigation/native
) to create a switch:import { createSwitchNavigator } from '@react-navigation/compat';
const Switch = createSwitchNavigator();
const MySwitch = () => (
<Switch.Navigator>
<Switch.Screen name="Login" component={LoginScreen} />
<Switch.Screen name="Main" component={MainScreen} />
</Switch.Navigator>
);
Custom Navigation involves creating your own navigation components or modifying existing navigators to fit specific app requirements.
import { createNavigatorFactory } from '@react-navigation/native';
const CustomNavigator = createNavigatorFactory(MyCustomNavigator);
const MyCustomNavigator = ({ state, descriptors, navigation }) => {
// Custom rendering logic
};
useNavigationState
and navigation.dispatch
for more complex scenarios.CardStyleInterpolators
from React Navigation.React Native provides built-in support for making network requests using the fetch
API. This allows you to communicate with RESTful services and retrieve or send data.
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
POST
method to send data to an API:fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ key: 'value' }),
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));
fetch('https://api.example.com/data')
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Fetch error:', error));
Local storage is used to store data persistently on the device. React Native provides the AsyncStorage
API for this purpose.
AsyncStorage.setItem
to store a key-value pair:import AsyncStorage from '@react-native-async-storage/async-storage';
const storeData = async (value) => {
try {
await AsyncStorage.setItem('@storage_key', value);
} catch (e) {
// saving error
}
};
AsyncStorage.getItem
to retrieve stored data:const getData = async () => {
try {
const value = await AsyncStorage.getItem('@storage_key');
if (value !== null) {
console.log(value);
}
} catch (e) {
// error reading value
}
};
const removeData = async () => {
try {
await AsyncStorage.removeItem('@storage_key');
} catch (e) {
// error removing value
}
};
The Camera Roll API allows you to access the device's photo library, allowing you to retrieve images and videos.
npm install @react-native-community/cameraroll
CameraRoll.getPhotos
to fetch images:import CameraRoll from '@react-native-community/cameraroll';
const fetchPhotos = async () => {
try {
const photos = await CameraRoll.getPhotos({
first: 20,
assetType: 'Photos',
});
console.log(photos.edges);
} catch (error) {
console.error(error);
}
};
CameraRoll.save
to save images to the camera roll:const saveImage = async (uri) => {
try {
await CameraRoll.save(uri);
console.log('Image saved successfully!');
} catch (error) {
console.error('Error saving image:', error);
}
};
The Geolocation API allows you to access the device's location and provides information about the user's current position.
navigator.geolocation.getCurrentPosition
:navigator.geolocation.getCurrentPosition(
position => {
const { latitude, longitude } = position.coords;
console.log('Location:', latitude, longitude);
},
error => console.error(error),
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 }
);
navigator.geolocation.watchPosition
to track location changes:const watchId = navigator.geolocation.watchPosition(
position => {
const { latitude, longitude } = position.coords;
console.log('Updated Location:', latitude, longitude);
},
error => console.error(error),
{ enableHighAccuracy: true, distanceFilter: 1 }
);
navigator.geolocation.clearWatch(watchId);
The Animations API allows you to create fluid animations in your React Native applications, enhancing user experience.
import { Animated } from 'react-native';
const fadeAnim = useRef(new Animated.Value(0)).current;
const fadeIn = () => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 1000,
useNativeDriver: true,
}).start();
};
<Animated.View style={{ opacity: fadeAnim }}>
<Text>Hello, World!</Text>
</Animated.View>
The Accessibility API provides tools and components to make your app accessible to users with disabilities.
<Button
title="Click Me"
accessibilityLabel="Click to perform an action"
accessibilityHint="This button performs a special action"
onPress={() => {}} />
<TouchableOpacity
accessible={true}
accessibilityRole="button"
accessibilityLabel="Press to open settings"
onPress={() => {}}>
<Text>Settings</Text>
</TouchableOpacity>
The Context API provides a way to share values like state and functions between components without having to pass props manually through every level of the tree.
React.createContext()
:import React, { createContext, useState } from 'react';
const MyContext = createContext();
const MyProvider = ({ children }) => {
const [state, setState] = useState('default value');
return (
<MyContext.Provider value={{ state, setState }}>
{children}
</MyContext.Provider>
);
};
useContext
hook to access context values:import React, { useContext } from 'react';
const MyComponent = () => {
const { state, setState } = useContext(MyContext);
return <Text>{state}</Text>;
};
const App = () => (
<MyProvider>
<MyComponent />
</MyProvider>
);
Redux is a predictable state container for JavaScript apps. It helps you manage the application state in a central store.
npm install redux react-redux
import { createStore } from 'redux';
const initialState = { count: 0 };
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
default:
return state;
}
};
const store = createStore(reducer);
Provider
component to pass the store to your app:import { Provider } from 'react-redux';
const App = () => (
<Provider store={store}>
<MyComponent />
</Provider>
);
connect
to connect components to the Redux store:import { connect } from 'react-redux';
const MyComponent = ({ count, increment }) => (
<View>
<Text>Count: {count}</Text>
<Button title="Increment" onPress={increment} />
</View>
);
const mapStateToProps = (state) => ({
count: state.count,
});
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch({ type: 'INCREMENT' }),
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
MobX is a simple, scalable state management solution. It uses observables to manage state and reactivity.
npm install mobx mobx-react
observable
:import { makeAutoObservable } from 'mobx';
class Store {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
}
const store = new Store();
Provider
component to pass the store:import { Provider } from 'mobx-react';
const App = () => (
<Provider store={store}>
<MyComponent />
</Provider>
);
observer
HOC to react to state changes:import { observer } from 'mobx-react';
const MyComponent = observer(({ store }) => (
<View>
<Text>Count: {store.count}</Text>
<Button title="Increment" onPress={() => store.increment()} />
</View>
));
Redux Saga is a middleware library that aims to make handling side effects in Redux easier and more manageable using sagas.
npm install redux-saga
import { call, put, takeEvery } from 'redux-saga/effects';
function* fetchData() {
const response = yield call(fetch, 'https://api.example.com/data');
const data = yield response.json();
yield put({ type: 'DATA_RECEIVED', payload: data });
}
function* mySaga() {
yield takeEvery('FETCH_DATA', fetchData);
}
import createSagaMiddleware from 'redux-saga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(mySaga);
Redux Thunk is a middleware that allows you to write action creators that return a function instead of an action.
npm install redux-thunk
import { ThunkAction } from 'redux-thunk';
const fetchData = (): ThunkAction<Promise<void>, RootState, unknown, Action> => async dispatch => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
dispatch({ type: 'DATA_RECEIVED', payload: data });
};
import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
const store = createStore(reducer, applyMiddleware(thunk));
Apollo Client is a comprehensive state management library for JavaScript that enables you to manage both local and remote data with GraphQL.
npm install @apollo/client graphql
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://api.example.com/graphql',
cache: new InMemoryCache(),
});
ApolloProvider
to wrap your application:import { ApolloProvider } from '@apollo/client';
const App = () => (
<ApolloProvider client={client}>
<MyComponent />
</ApolloProvider>
);
useQuery
hook to fetch data:import { useQuery, gql } from '@apollo/client';
const GET_DATA = gql`
query GetData {
data {
id
value
}
}
`;
const MyComponent = () => {
const { loading, error, data } = useQuery(GET_DATA);
if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return <Text>Data: {JSON.stringify(data)}</Text>;
};
Setting up a testing environment is crucial for ensuring that your React Native applications are stable and function as expected. This typically involves installing testing libraries and configuring them properly.
npm install --save-dev jest react-test-renderer @testing-library/react-native enzyme enzyme-adapter-react-16
jest.config.js
file in the root of your project to configure Jest:module.exports = {
preset: 'react-native',
setupFiles: ['/jest/setup.js'],
transform: {
'^.+\\.js$': 'babel-jest',
},
testPathIgnorePatterns: ['/node_modules/', '/android/', '/ios/'],
};
import '@testing-library/jest-native/extend-expect';
Jest is a delightful JavaScript testing framework with a focus on simplicity. It works well with React Native.
MyComponent.test.js
):import React from 'react';
import { render } from '@testing-library/react-native';
import MyComponent from './MyComponent';
test('renders correctly', () => {
const { getByText } = render(<MyComponent />);
expect(getByText('Hello, World!')).toBeTruthy();
});
npm test
Enzyme is a JavaScript testing utility for React that makes it easier to test React components.
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
test('renders correctly', () => {
const wrapper = shallow(<MyComponent />);
expect(wrapper.find('Text').prop('children')).toEqual('Hello, World!');
});
Testing components ensures that they behave as expected. This includes rendering, user interactions, and state changes.
import { fireEvent } from '@testing-library/react-native';
test('button click increments count', () => {
const { getByText } = render(<MyComponent />);
fireEvent.press(getByText('Increment'));
expect(getByText('Count: 1')).toBeTruthy();
});
test('initial state is 0', () => {
const { getByText } = render(<MyComponent />);
expect(getByText('Count: 0')).toBeTruthy();
});
Snapshot testing is a feature of Jest that allows you to test the rendered output of your components.
import React from 'react';
import renderer from 'react-test-renderer';
import MyComponent from './MyComponent';
test('matches snapshot', () => {
const tree = renderer.create(<MyComponent />).toJSON();
expect(tree).toMatchSnapshot();
});
npm test -u
Detox is an end-to-end testing framework for React Native applications that provides a way to test the user interface and its interactions.
npm install --save-dev detox
detox init -r jest
package.json
:"detox": {
"configurations": {
"ios.sim": {
"binaryPath": "path/to/your/app",
"build": "xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath build",
"type": "ios.simulator",
"device": {
"type": "iPhone 11"
}
}
}
}
describe('Example', () => {
beforeAll(async () => {
await device.reloadReactNative();
});
it('should show the welcome screen', async () => {
await expect(element(by.text('Welcome'))).toBeVisible();
});
});
detox test -c ios.sim
App bundling is the process of packaging your React Native application into a format that can be distributed and run on mobile devices. This includes optimizing resources, compressing assets, and generating the final executable files.
react-native bundle --platform ios --dev false --entry-file index.js --bundle-output ios/main.jsbundle --assets-dest ios
--dev false
flag to avoid including development-only code in the final bundle.Deploying to the App Store requires several steps to ensure compliance with Apple's guidelines and proper submission of your app.
cd ios
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -archivePath MyApp.xcarchive archive
altool --upload-app -f MyApp.ipa -u "your-apple-id" -p "app-specific-password"
Deploying to the Google Play Store involves preparing your app for Android devices and submitting it through the Google Play Console.
cd android
./gradlew assembleRelease
build.gradle
file with your signing information.CodePush is a cloud service that allows you to deploy mobile app updates directly to users' devices without going through the app store review process.
npm install --save react-native-code-push
code-push release-react MyApp ios -d Production
codePush.checkForUpdate().then((update) => {
if (update) {
codePush.sync();
}
});
Fastlane is an open-source tool that automates tedious tasks like building, testing, and releasing apps.
sudo gem install fastlane -NV
fastlane init
Fastfile
to automate tasks:lane :build do
gradle(task: 'assembleRelease')
end
lane :deploy do
upload_to_play_store
upload_to_app_store
end
fastlane build
Sentry is an error tracking tool that helps monitor and fix crashes in real time.
npm install --save @sentry/react-native
import * as Sentry from '@sentry/react-native';
Sentry.init({
dsn: 'YOUR_SENTRY_DSN',
});
Sentry.captureException(new Error('Something went wrong!'));
Sentry.startTransaction({ name: 'My Transaction' });
// Add your code logic
transaction.finish();
Setting up TypeScript in a React Native project enhances code quality by providing static type-checking, leading to fewer runtime errors and improved maintainability.
npx react-native init MyApp --template react-native-template-typescript
tsconfig.json
, which you can customize as needed.tsconfig.json
has the following key settings:{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"jsx": "react-native",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"]
}
npm install --save-dev typescript @types/react @types/react-native
.js
to .tsx
to leverage TypeScript features.TypeScript provides several basic types that can be used to ensure your variables and functions are type-safe.
number
- For numbers (e.g., let age: number = 30;
)string
- For text (e.g., let name: string = "John";
)boolean
- For true/false values (e.g., let isActive: boolean = true;
)null
and undefined
- For absence of value (e.g., let data: null = null;
)any
- To opt-out of type-checking (use sparingly, e.g., let result: any = "hello";
)let numbers: number[] = [1, 2, 3]; // Array of numbers
let tuple: [string, number] = ["John", 30]; // Tuple type
type User = {
name: string;
age: number;
};
let user: User = { name: "Alice", age: 25 };
Both interfaces and types allow you to define the shape of an object, but they have some differences.
interface User {
name: string;
age: number;
}
function greet(user: User) {
return `Hello, ${user.name}`;
}
type StringOrNumber = string | number;
let id: StringOrNumber = "abc123"; // Valid
id = 123; // Also valid
interface Person {
name: string;
}
interface Employee extends Person {
employeeId: number;
}
TypeScript supports object-oriented programming with class-based structures, providing additional features compared to JavaScript.
class Car {
private model: string;
constructor(model: string) {
this.model = model;
}
getModel() {
return this.model;
}
}
extends
to create a derived class:class ElectricCar extends Car {
private battery: number;
constructor(model: string, battery: number) {
super(model);
this.battery = battery;
}
getBattery() {
return this.battery;
}
}
public
, private
, and protected
modifiers to control access to class members.Generics allow you to create reusable components or functions that work with any data type.
function identity(arg: T): T {
return arg;
}
let output = identity("Hello"); // output is of type string
interface Box {
contents: T;
}
let stringBox: Box = { contents: "Hello" };
let numberBox: Box = { contents: 123 };
class Wrapper {
value: T;
constructor(value: T) {
this.value = value;
}
}
let stringWrapper = new Wrapper("Wrapped!");
Testing in a TypeScript environment with Jest requires setting up Jest and writing tests that leverage TypeScript’s type safety.
npm install --save-dev jest ts-jest @types/jest
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
.test.ts
or .spec.ts
extension:test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
npx jest
Creating custom native modules for Android allows you to extend React Native's capabilities by integrating Android-specific features. This involves writing Java or Kotlin code.
ReactContextBaseJavaModule
and implement the required methods.package com.example;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class MyModule extends ReactContextBaseJavaModule {
MyModule(ReactApplicationContext context) {
super(context);
}
@Override
public String getName() {
return "MyModule";
}
@ReactMethod
public void customMethod(String message) {
// Your custom native code here
}
}
package com.example;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyPackage implements ReactPackage {
@Override
public List createNativeModules(ReactApplicationContext reactContext) {
List modules = new ArrayList<>();
modules.add(new MyModule(reactContext));
return modules;
}
@Override
public List createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
import { NativeModules } from 'react-native';
const { MyModule } = NativeModules;
MyModule.customMethod('Hello from React Native!');
Custom native modules for iOS enable integration with Objective-C or Swift. This allows you to access iOS-specific APIs and functionality.
RCTBridgeModule
.#import
@interface MyModule : NSObject <RCTBridgeModule>
@end
@implementation MyModule
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(customMethod:(NSString *)message) {
// Your custom native code here
}
@end
import { NativeModules } from 'react-native';
const { MyModule } = NativeModules;
MyModule.customMethod('Hello from React Native!');
Custom native modules for Windows enable you to leverage Windows-specific APIs and capabilities in your React Native app.
IReactModule
interface.using Microsoft.ReactNative;
using System;
namespace MyApp
{
public class MyModule : IReactModule
{
[ReactMethod]
public void CustomMethod(string message)
{
// Your custom native code here
}
}
}
using Microsoft.ReactNative;
namespace MyApp
{
public class MyModulePackage : IReactPackage
{
public void CreatePackage(IReactPackageBuilder packageBuilder)
{
packageBuilder.AddModule();
}
}
}
import { NativeModules } from 'react-native';
const { MyModule } = NativeModules;
MyModule.customMethod('Hello from React Native!');
A countdown timer can be implemented as a custom native module that provides precise timing functionality.
package com.example;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
public class CountdownTimerModule extends ReactContextBaseJavaModule {
CountdownTimerModule(ReactApplicationContext context) {
super(context);
}
@ReactMethod
public void startTimer(int seconds, Promise promise) {
new CountDownTimer(seconds * 1000, 1000) {
public void onTick(long millisUntilFinished) {
// Emit tick event
}
public void onFinish() {
promise.resolve("Timer Finished");
}
}.start();
}
}
#import
@interface CountdownTimer : NSObject <RCTBridgeModule>
@end
@implementation CountdownTimer {
NSTimer *_timer;
}
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(startTimer:(NSInteger)seconds resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
_timer = [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(timerFinished:) userInfo:nil repeats:NO];
}
- (void)timerFinished:(NSTimer *)timer {
resolve(@"Timer Finished");
[_timer invalidate];
}
A custom video player can enhance your React Native app by integrating platform-specific video playback features.
package com.example;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class VideoPlayerModule extends ReactContextBaseJavaModule {
VideoPlayerModule(ReactApplicationContext context) {
super(context);
}
@ReactMethod
public void playVideo(String url) {
// Implement ExoPlayer setup and playback
}
}
#import
#import
@interface VideoPlayer : NSObject <RCTBridgeModule>
@end
@implementation VideoPlayer
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(playVideo:(NSString *)url) {
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
controller.player = [AVPlayer playerWithURL:[NSURL URLWithString:url]];
// Present the player
}
@end
Creating a custom image picker module allows users to select images from their device.
package com.example;
import android.content.Intent;
import android.net.Uri;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
public class ImagePickerModule extends ReactContextBaseJavaModule {
ImagePickerModule(ReactApplicationContext context) {
super(context);
}
@ReactMethod
public void openImagePicker(Promise promise) {
Intent intent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
// Start activity for result
}
}
#import
#import
@interface ImagePicker : NSObject <RCTBridgeModule>
@end
@implementation ImagePicker
RCT_EXPORT_MODULE();
RCT_EXPORT_METHOD(openImagePicker:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
// Present the picker
}
@end
Performance optimization in React Native involves enhancing the app's speed, responsiveness, and overall user experience. Key areas of focus include minimizing load times, reducing CPU usage, and optimizing memory consumption.
JavaScript performance directly impacts the responsiveness of your React Native app. Optimizing JavaScript execution can help prevent jank and ensure smooth user interactions.
shouldComponentUpdate
and React.memo
to prevent unnecessary re-renders of components.Efficient rendering is crucial for maintaining smooth animations and transitions in your app. Here are some strategies to optimize rendering:
Native optimization involves enhancing the performance of the native components that React Native relies on. This can be achieved through:
useNativeDriver
option in the Animated API to offload animations to the native thread.Network optimization is essential for improving the performance of your app when fetching data. Key strategies include:
AsyncStorage
or libraries such as react-query
to cache API responses and reduce redundant network calls.Hermes is an open-source JavaScript engine optimized for running React Native applications on Android. It improves performance by reducing app size and enhancing startup times.
android/app/build.gradle
:project.ext.react = [
enableHermes: true, // Enable Hermes
]