React Native has come a long way, and with the latest versions offering improved performance and TypeScript-first development, it’s a great time to dive into building robust apps with navigation and API communication in mind.
In this article, we’ll explore:
- Setting up React Navigation in a TypeScript project
- Working with Stacks and Tabs
- Managing API calls with
axios
anduseEffect
- Handling loading, error, and data states
- Bonus: Tips for organizing your codebase!
Prerequisites
- Node.js 18+
- React Native CLI or Expo (we'll go with bare React Native here)
- React Native 0.76+
- TypeScript
- Basic understanding of React Native components
Setup React Navigation
First, install the required dependencies:
npm install @react-navigation/native
npm install react-native-screens react-native-safe-area-context react-native-gesture-handler react-native-reanimated
npx pod-install
Then install the stack and bottom tab navigators:
npm install @react-navigation/native-stack @react-navigation/bottom-tabs
Update babel.config.js
for reanimated (if not Expo):
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: ['react-native-reanimated/plugin'],
};
Create Navigation Structure
navigation/RootNavigator.tsx
import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import HomeScreen from '../screens/HomeScreen';
import DetailsScreen from '../screens/DetailsScreen';
export type RootStackParamList = {
Home: undefined;
Details: { userId: number };
};
const Stack = createNativeStackNavigator<RootStackParamList>();
const RootNavigator = () => (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
export default RootNavigator;
Create Screen Components for Home Screen
screens/HomeScreen.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, Button, FlatList, ActivityIndicator } from 'react-native';
import axios from 'axios';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import { RootStackParamList } from '../navigation/RootNavigator';
type Props = NativeStackScreenProps<RootStackParamList, 'Home'>;
interface User {
id: number;
name: string;
}
const HomeScreen: React.FC<Props> = ({ navigation }) => {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
axios.get<User[]>('https://jsonplaceholder.typicode.com/users')
.then(response => setUsers(response.data))
.catch(err => setError(err.message))
.finally(() => setLoading(false));
}, []);
if (loading) return <ActivityIndicator />;
if (error) return <Text>Error: {error}</Text>;
return (
<FlatList
data={users}
keyExtractor={item => item.id.toString()}
renderItem={({ item }) => (
<View style={{ padding: 10 }}>
<Text>{item.name}</Text>
<Button
title="View Details"
onPress={() => navigation.navigate('Details', { userId: item.id })}
/>
</View>
)}
/>
);
};
export default HomeScreen;
Create Screen Components for Detail Screen
screens/DetailsScreen.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import { NativeStackScreenProps } from '@react-navigation/native-stack';
import axios from 'axios';
import { RootStackParamList } from '../navigation/RootNavigator';
type Props = NativeStackScreenProps<RootStackParamList, 'Details'>;
interface UserDetail {
id: number;
name: string;
email: string;
phone: string;
}
const DetailsScreen: React.FC<Props> = ({ route }) => {
const { userId } = route.params;
const [user, setUser] = useState<UserDetail | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
axios.get<UserDetail>(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then(response => setUser(response.data))
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <ActivityIndicator />;
if (!user) return <Text>No user found</Text>;
return (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 18 }}>{user.name}</Text>
<Text>{user.email}</Text>
<Text>{user.phone}</Text>
</View>
);
};
export default DetailsScreen;
Organize API Layer (Optional but Recommended)
Create a file api/client.ts
:
import axios from 'axios';
const client = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com/',
timeout: 5000,
});
export default client;
Then import and use client.get()
instead of axios.get()
.
Project Structure Suggestion
/src
/navigation
RootNavigator.tsx
/screens
HomeScreen.tsx
DetailsScreen.tsx
/api
client.ts
/types
index.ts
App.tsx
Final Thoughts
- React Navigation’s TypeScript support makes route safety a breeze.
- Centralizing API calls helps scale and debug faster.
- Handling loading/error/data states improves user experience.
- Organize your folders early—it pays off later!
That's it for today. In the next part of this series, we’ll explore advanced topics like redux toolkit and it's implementation.
Feel free to reach out to me if you have any questions or need assistance.
LinkedIn: https://www.linkedin.com/in/rushikesh-pandit-646834100/
GitHub: https://github.com/rushikeshpandit
Portfolio: https://www.rushikeshpandit.in
#ReactNative #TypeScript #MobileDevelopment #SoftwareEngineering #DevCommunity
Top comments (0)