Learning Objectives
By the end of this section, you will be able to:
- Create a Todo native mobile application with React Native or Node
- Create a React Native app and its components
- Connect the front-end Native app with the back-end Node app
In the previous sections, you worked with Bootstrap, Django, React, and Node to build versions of a Todo web application. In this section, you will learn how to use React Native and Node to develop a Todo application for mobile devices. You are already familiar with Node. Like React, React Native is an open-source JavaScript framework used to build native applications for mobile devices.
Link to Learning
React Native uses the React JavaScript library to enable native development and build user interfaces for mobile devices. React Native has the flexibility to work with Xcode, which is the IDE for various platforms. Released by Facebook (now Meta) in 2015, React Native has become increasingly popular among developers.
Creating a Todo Web Application with React Native and Node
This application uses React Native and Node to implement a simple Todo mobile application. This application builds on top of the React and Node application discussed in 11.3 Sample Responsive WAD with Bootstrap/React and Node. For this version of the application, React Native serves as the front end handling the user interface and getting and setting data via HTTP requests. Node serves as the back end that makes use of the API built using the Django REST Framework in 11.2 Sample Responsive WAD with Bootstrap and Django.
Prerequisites
To build the Todo mobile application using React Native and Node, you need React Native v0.67, Node v14.17.5, ExpressJS v4.17.2, MongooseJS v6.1.9, and Axios v0.21.0. This Todo application will run using the Android emulator and will use Android Studio v2021.1.1.
Link to Learning
Xcode is the IDE for the Apple platform. For Apple, React Native provides developers with the tools needed for cross-platform development that creates user-friendly apps on mobile devices.
- To begin, download and install an emulator for your intended platform (iOS or Android).
- If you plan to run the native app on an iOS device or emulator, download and install Xcode, which is Apple’s IDE that enables application development for Apple’s platforms.
- If you plan to run the native app on an Android device or emulator, download and install Android Studio, which enables application development for Android mobile operating systems.
- Figure 11.38 shows how to set up the Android emulator. Launch Android Studio. From the top navigation, select Tools > Device Manager. Click on Create device. In the Select Hardware pop-up, select Pixel 5 and click Next. On the next page, select Pie Download. Click on the Download link to obtain an image as shown. Click Next followed by Finish.
Once the image has been created, it will appear in the Device Manager. Next, click on the Start button to launch the emulator. When the emulator is launched, it will turn on, as shown in Figure 11.39.
Link to Learning
Xcode also integrates well with Android Studio for developing Android apps. Android Studio is the IDE for Android devices.
Creating the React Native App
After launching the emulator, create the React Native app by running the following command in a directory outside and separate from the nodebackend/ directory. Figure 11.40 shows the React Native application files in the reactnativefrontend/ directory.
$ npx react-native init reactnativefrontend
Next, navigate into the reactnativefrontend/ directory and launch the React Native application to confirm that the React Native front-end application has been created successfully. At this point, the application is not connected to the Node back end and will launch the Metro bundler, which bundles the JavaScript code that is deployed on the mobile device or emulator when the React Native front-end application is successfully completed. When this is done, run the command. Figure 11.41 shows the page in the terminal.
$ npx react-native start
The next step is to open another terminal, navigate to the reactnativefrontend/ directory, and run the following command. This will build the front-end code and deploy it on the emulator, which may take a few minutes.
$ npx react-native run-android
When the application is successfully built, Figure 11.42 shows what should appear in the second terminal.
Figure 11.43 shows how the native application should appear in the emulator.
Creating the React Native App Components
The packages used by the React Native app for navigation and other features are different from the packages used by React. To install the required packages, in the reactnativefrontend/ directory, run the following list of commands:
$ npm install @react-navigation/native@6.0.7
$ npm install @react-navigation/native-stack@6.3.0
$ npm install axios@0.25.0
$ npm install moment@2.29.1
$ npm install react-native-modal@13.0.0
$ npm install react-native-safe-area-context@3.3.2
$ npm install react-native-screens@3.10.2
$ npm install react-native-snackbar@2.4.0
$ npm install react-native-vector-icons@9.0.0
The first step is to create the Todo component. In the reactnativefrontend/ directory, create the directory src/Screens/. In the src/Screens/ directory, create the file TodoList.js. The following code shows the imports required to create the screen components.
import React, {useState, useEffect} from 'react';
import {
View,
Text,
StyleSheet,
TextInput,
FlatList,
TouchableOpacity,
Dimensions,
StatusBar,
Alert,
} from 'react-native';
import axios from 'axios';
import {COLORS} from '../utils/colors';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
import Modal from 'react-native-modal';
import Snackbar from 'react-native-snackbar';
import {duration} from 'moment';
Once this is done, the following code includes the return()
function that renders the screen components.
return (
<View style={styles.container}>
<StatusBar backgroundColor={COLORS.DARKALT} />
<Text style={styles.heading}>Welcome to Todo List App!</Text>
<Text style={styles.heading}>Your Tasks</Text>
{todos.length == 0 ? (
<View style={styles.center}>
<MaterialCommunityIcon
name="note-multiple"
size={90}
color={COLORS.LIGHTALT}
/>
<Text style={styles.noTasksText}>No Tasks Added</Text>
</View>
) : (
<>
<View style={styles.todosContainer}>
<FlatList
data={todos}
renderItem={({item}) => (
<View style={styles.todo}>
<Text
style={item.complete ? styles.completedStyle : styles.text}>
{item.title}
</Text>
<TouchableOpacity
style={styles.deleteTodo}
onPress={() => deleteTodo(item._id)}>
<Text style={{color: COLORS.WHITE}}>X</Text>
</TouchableOpacity>
</View>
)}
/>
</View>
<TouchableOpacity
style={styles.addButton}
onPress={() => {
setModalActive(true);
}}>
<MaterialIcon name="add" size={32} color={COLORS.LIGHT} />
</TouchableOpacity>
</>
)}
<Modal
isVisible={modalActive}
animationIn={'slideInUp'}
animationOut={'slideInDown'}>
<View style={styles.modalView}>
<TouchableOpacity
style={styles.modalCloseBtn}
onPress={() => setModalActive(false)}>
<Text style={styles.modalCloseBtnText}>X</Text>
</TouchableOpacity>
<Text style={styles.addTaskHeading}>Add Task</Text>
<TextInput
style={styles.taskInput}
placeholder="Enter task here.."
onChangeText={text => setNewTodo(text)}
value={newTodo}
/>
<TouchableOpacity style={styles.createBtn} onPress={() => addTodo()}>
<Text style={styles.createBtnText}>Create Task</Text>
</TouchableOpacity>
</View>
</Modal>
</View>
);
The following code creates the modal pop-up screen that is used to create a Todo list.
<Modal
isVisible={modalActive}
animationIn={'slideInUp'}
animationOut={'slideInDown'}>
<View style={styles.modalView}>
<TouchableOpacity
style={styles.modalCloseBtn}
onPress={() => setModalActive(false)}>
<Text style={styles.modalCloseBtnText}>X</Text>
</TouchableOpacity>
<Text style={styles.addTaskHeading}>Add Task</Text>
<TextInput
style={styles.taskInput}
placeholder="Enter task here.."
onChangeText={text => setNewTodo(text)}
value={newTodo}
/>
<TouchableOpacity style={styles.createBtn} onPress={() => addTodo()}>
<Text style={styles.createBtnText}>Create Task</Text>
</TouchableOpacity>
</View>
</Modal>
Next, update the reactnativefrontend/App.js file, as shown in the following code, to render the screen components declared in the TodoList.js file.
import React from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import TodoList from './src/Screens/TodoList';
const Stack = createNativeStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="TodoList"
component={TodoList}
options={{headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
When this is done, relaunch the Metro bundler and deploy the native application by running the following commands:
$ npx react-native start $ npx react-native run-android
Next, create a Todo item (Figure 11.44).
After the Todo item is created, it should appear on the main screen as seen in Figure 11.45.
Connecting the Front-End Native App with the Back-End Node App
The next step is to connect the front-end React Native app with the back-end Node app. To do this, open reactnativefrontend/src/Screens/Todolist.js. Add the API_BASE variable and configure the IP address to the local computer, running the Express web server, as shown in the following code.
import axios from 'axios';
import {COLORS} from '../utils/colors';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
import MaterialCommunityIcon from 'react-native-vector-icons/MaterialCommunityIcons';
import Modal from 'react-native-modal';
import Snackbar from 'react-native-snackbar';
import {duration} from 'moment';
const width = Dimensions.get('window').width;
const API_BASE = 'http://192.168.1.183:8080';
Next, add the Axios calls to interact with the REST API provided via the Express web server to interact with the MongoDB database.
const [todos, setTodos] = useState([]);
const [modalActive, setModalActive] = useState(false);
const [newTodo, setNewTodo] = useState('');
useEffect(() => {
GetTodos();
}, [todos]);
const GetTodos = () => {
axios
.get(`${API_BASE}/api/todos`)
.then(response => {
setTodos(response.data);
})
.catch(err => {
console.error('Error: ', err);
Snackbar.show({
text: '' + err,
duration: Snackbar.LENGTH_LONG,
backgroundColor: 'red',
textColor: COLORS.WHITE,
});
});
};
const completeTodo = async id => {
const data = await axios.put(`${API_BASE}/api/todos/${id}`);
setTodos(todos =>
todos.map(todo => {
if (todo._id === data._id) {
todo.complete = data.complete;
}
return todo;
}),
);
};
const deleteTodo = async id => {
const data = await axios.delete(`${API_BASE}/api/todos/${id}`);
setTodos(todos => todos.filter(todo => todo._id !== data._id));
};
const addTodo = async () => {
if (newTodo == '') {
Alert.alert('Error!', 'Please enter a task first!');
return;
} else {
await axios
.post(`${API_BASE}/todo/new`, {
text: newTodo,
})
.then(function (response) {
const data = response.data;
setTodos([...todos, data]);
setModalActive(false);
setNewTodo('');
})
.catch(function (error) {
console.log('Error: ', error);
});
}
};