I was once asked by a client to convert an existing data-driven web
application to a mobile application. To the client, it sounded as easy as
simply clicking a button to do the required conversion. Unknown and
unbelievable to the client, I would have to sit down and develop a new system
(or two new systems to be able to use both iOS and Android). To sum it up, it
is not as easy and cheap as it might appear to develop a mobile app even if
there is already a corresponding web app.
The requested mobile app would have to make HTTP requests to
perform CRUD operations on the backend. REST API comes to a developer’s mind
immediately. The only advantage - perhaps - was that I already have the necessary endpoints in the backend. When I find an opportunity later, I will make a post about offline-first mobile implementation, but for now it is worth knowing that it isn't easy sailing.
But the user also naturally wanted offline capabilities of the mobile application - which was understandable. However, this only makes development more complicated and costly. Offline capabilities wound ensure that the user would continue entering or accessing data even without connectivity; it would also make saving data faster because that would be done locally, right?
The development of mobile apps that is platform-agnostic has
not been any easier until the advent of React Native. For instance, prior to
React Native, mobile apps would have to be developed separately for Androids
(using Java or Kotlin) and iOS (using Objective-C or Swift). With React Native,
one can develop an application that would run on both platforms.
React Native is a UI development library that uses
Javascript and React functionalities. Both React (ReactJS) and React Native use
the concepts of props, state, components and JSX. However, React Native uses
native components (<View>, <Text>, <TextInput>,
<Image>, etc) while ReactJS would use web components or HTML code
(<div>, <p>, <input>, <img>, etc) as corresponding tags
for rendering.
For development, we can use Expo Client or Expo Snacks (web)
for the development and simulation of iOS and Android mobile app.
1. Using Expo Client (expo-cli) - the command line interface between developer and expo tools
- node must be installed (to confirm, check for node version: node –v)
- check react-native version: react-native -v
Install expo-cli (npm install --global expo-cli)
- Download expo-cli
- open the command prompt and run it as administrator.
- recommended npm uninstall --global expo-cli.
- run the command npm cache clean --force.
- run the command npm cache verify .
- now run the command npm install --global expo-cli (also used
to upgrade to latest version)
Run expo-cli
At the project folder, type: npm start
Server starts and a browser page opens
For iOS and Android, use Expo Go to run apps that are served through Expo Cli
Common issues with expo-cli
If ExpoGo is not starting on phone (just showing icon). Try
to resolve using the following in phone: Settings/Apps/ExpoGo/Storage/ClearData
If you get message “something went wrong”: Make sure the
computer and phone are connected on same wifi (understand this clearly)
2. Expo Snacks (web version)
https://snack.expo.dev/@rorama
Soft landing
For a soft landing, a simple “Hello World” example mobile application code below might be of help
import React from 'react';
import { Text, View } from 'react-native';
const HelloWorldApp = () => {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center"
}}>
<Text>Hello, world!</Text>
</View>
)
}
export default HelloWorldApp;
API Call implementation using Fetch
Now that we are done with the basics of React Native
environment setup and Hello World code sample, let us go to REST API
implementation. In this case, we assume that we have a resource on some other
domain that we want to consume. In real life, our mobile app cannot itself
contain every resource that it requires. Therefore, from time to time we have
to make API calls to retrieve or update resources from elsewhere.
In this post, I will simply show the HTTP request/response
cycle in React Native with a REST API implementation. Once again, React Native
provides the Fetch API for networking needs i.e. for accessing or making
changes to resources. The Fetch
implementation is as simple as the one used in Vanilla Javascript.
A full self-explanatory, multi-component example of a REST
API call with React Native is below, but it requires basic understanding of React Native (props, state, components ...). Note that I have shared two versions of the API call for comparison: FetchApi (using procedural method) and FetchApiClass (using OOP method). I hope this helps.
App.js - the main application
import React, { useState } from "react"; //Import React and useState hook
import { StyleSheet, View, Button, FlatList, Image, Alert, TouchableOpacity } from "react-native"; //Import React native components
//Import these components of the project into the app component
import FetchApi from "./components/FetchApi";
import FetchApiClass from "./components/FetchApiClass";
export default function App() {
//Return the components while passing props to each component
return (
<View>
<Header title="Fetch API Call"></Header>
<View style={styles.screen}>
<FetchApi/> //using procedural method
</View>
<View style={styles.screen}>
<FetchApiClass/> //using OOP method
</View>
</View>
);
}
//Add styling to components using StyleSheet
const styles = StyleSheet.create({
screen: {
paddingTop: 70,
paddingHorizontal: 70
},
screenlist: {
marginLeft: 20,
marginRight: 20,
backgroundColor: 'inherit',
color: "red",
paddingLeft: 20,
paddingRight: 20,
},
});
Header.js- for displaying header title
import React from "react"; //import React
import { StyleSheet, View, Text } from "react-native"; //import React Native components
//Return Header text along with style
const Header = props => {
return (
<View style={styles.header}>
<Text style={styles.headerTitle}>{props.title}</Text>
</View>
);
};
//Add style to create the Header using StyleSheet
const styles = StyleSheet.create({
header: {
width: "100%",
height: 50,
paddingTop: 0,
backgroundColor: "purple",
alignItems: "center",
justifyContent: "center"
},
headerTitle: {
color: "white",
fontSize: 20
}
});
export default Header; //Export the component
FetchApi.js - fetching data from API using procedural method
import React, { useEffect, useState } from 'react';
import { FlatList, Text, View, Button } from 'react-native';
const FetchApi = props => { //this is a component
const [isLoading, setLoading] = useState(true); //variable (state) and method for updating state respectively in square brackets
const [data, setData] = useState([]); //array variable (state) and method for updating states respectively in square brackets
console.log(data);
const url1 = 'https://rorama.github.io/data/mydata.json';
const headers = {}; // {mode: "no-cors"};
useEffect(() => { //to run immediately upon mounting
fetch(url1, {'headers': headers})
.then((response) => response.json())
.then((json) => {
setLoading(true);
setData(json) //this.setData({ data: json });
})
.catch((error) => console.error(error))
.finally(() => setLoading(false));
}, []);
/**
componentDidMount() { //same as useEffect - run at start
this.fetchCats();
}
*/
const ItemSeprator = () => <View style={{ //this is a component
height: 4,
width: "100%",
backgroundColor: "rgba(0,0,0,0.1)",
}} />
const handleRefresh = () => { //this is a component
this.setState({ refreshing: false }, ()=>{this.fetchCats()});
}
return (
<View style={{ flex: 1, padding: 24 }}>
{/* if isLoading, show Loading..., else show List */}
{isLoading ? <Text>Loading...</Text> :
( <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-between'}}>
<Text style={{ fontSize: 14, color: 'green', textAlign: 'center', paddingBottom: 10}}>{data.title} - Procedural</Text>
<ItemSeprator/>
<FlatList
data={data.articles}
keyExtractor = {({ id }, index) => id}
renderItem = {({ item }) => (
<>
<Text>{item.id + '. ' + item.title}</Text>
<ItemSeprator/>
<handleRefresh/>
</>
)}
/>
</View>
)}
</View>
);
};
export default FetchApi; //Export the component
FetchApiClass.js - fetching data from API using OOP method
import React, { useEffect, useState } from 'react';
import { FlatList, Text, View, Button } from 'react-native';
const FetchApiClass = props => { //this is a component
const [isLoading, setLoading] = useState(true); //variable (state) and method for updating state respectively in square brackets
const [data, setData] = useState([]); //array variable (state) and method for updating states respectively in square brackets
console.log(data);
const url1 = 'https://rorama.github.io/data/mydata.json';
const headers = {}; // {mode: "no-cors"};
const callFetch = (url) => {
fetch(url, {'headers': headers})
.then((response) => response.json())
.then((json) => {
setLoading(true);
setData(json) //this.setData({ data: json });
})
.catch((error) => alert(error))
.finally(() => setLoading(false));
};
const ItemSeprator = () => <View style={{ //this is a component
height: 4,
width: "100%",
backgroundColor: "rgba(0,0,0,0.1)",
}} />
const handleRefresh = () => { //this is a component
this.setState({ refreshing: false }, ()=>{this.fetchCats()});
}
return (
<View style={{ flex: 1, padding: 24 }}>
<Button title="Fetch API Class (OOP method)" onPress={() => callFetch(url1)}></Button> {/* button has no style */}
{/* if isLoading, show Loading..., else show List */}
{isLoading ? <Text>Loading...</Text> :
( <View style={{ flex: 1, flexDirection: 'column', justifyContent: 'space-between'}}>
<Text style={{ fontSize: 14, color: 'green', textAlign: 'center', paddingBottom: 10}}>{data.title} - OOP</Text>
<ItemSeprator/>
<FlatList
data={data.articles}
keyExtractor = {({ id }, index) => id}
renderItem = {({ item }) => (
<>
<Text>{item.id + '. ' + item.title}</Text>
<ItemSeprator/>
<handleRefresh/>
</>
)}
/>
</View>
)}
</View>
);
};
export default FetchApiClass; //Export the component
Sample output in simulator
No comments:
Post a Comment