Build Quotes App with React Native in 5 Minutes

September 02, 2019

Three days ago, I just give my first tech talk about "How to Get Started as a Mobile Developer" in Jakarta. I talk about what's going on with React Native this year, the good and bad things about it, what will change in the future, and how to get started with it. It was a very great experience and pretty much successful.

speaker

In the live coding session, I decided to build a quotes app with React Native, so that you can be inspired everyday with popular quotes. It's not a tutorial, but I just want to demonstrate people that they can build an app very fast (five minutes) with React Native.

livecoding

The UI is very straight forward, it shows the quote and the author inside text component, and a button to show another quote randomly from a fake REST API with JSON Server.

So, in this tutorial, I'm going to show you step-by-step on how to build that quotes app from my tech talk live coding demo. Enjoy!

Quotes REST API

As I mentioned previously, I use JSON Server to store and serve all of the quotes data. If you don't know what JSON Server is, basically it's just a fake REST API generator, created by this awesome dude, which we can use to serve a json file in an Node.js HTTP server.

Before we get started with it, we need to installed globally first by running this command inside your command-line.

$ npm install -g json-server

Then, we need a file that holds our data that looks something like this.

quotes.json
{
	"quotes": [
		{
			"text": "The Dream Is Free But the Hustle Is Sold Separately",
			"author": "George Koufalis"
		},
		{
			"text": "Mankind invented the atomic bomb, but no mouse would ever construct a mousetrap.",
			"author": "Albert Einstein"
		},
		{
			"text": "Talk is cheap. Show me the code.",
			"author": "Linus Torvalds"
		}
		// ...
	]
}

Here, we have an object that contains quotes property that represents array popular quotes from successful people. To serve this data, we can run json-server command inside our terminal.

$ json-server quotes.json

Initialize React Native Project

We have our server up and running, now it's time to work on the client side or our app. Here, we will use Expo, which is the most convenient way to start a React Native project. We also get several built-in native features like Camera, Location, File System, even Social Authentication.

First, you need to install Expo by running this command.

$ npm install -g expo-cli

This command will install the Expo CLI from npm package registry.

Second, initialize a new Expo project by running expo init command.

$ expo init <your-app-name>

Type your app name and your package name. Then, choose Blank template option.

Finally, open App.js and the starter code will looks something like this.

import React from "react"
import { StyleSheet, Text, View } from "react-native"

export default function App() {
	return (
		<View style={styles.container}>
			<Text>Open up App.js to start working on your app!</Text>
		</View>
	)
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: "#fff",
		alignItems: "center",
		justifyContent: "center",
	},
})

Now you can execute expo start in your terminal to run the app on a simulator/emulator, browser, or on a real device.

Fetch Random Quote

There are several approaches to fetch our quotes from the REST API. In this tutorial, we will going to use the new React feature called React Hooks. It's a handy feature which you can use to literally do everything that class component could do inside a functional component. More on that in this post.

function useQuote() {
	const [quote, setQuote] = React.useState(null)

	return quote
}

Here we define a useQuote function, which a React custom hook where we want to fetch random quote. The first thing we did here is to create a quote state with useState function from React.

Inside our App component, we can display our quote from the useQuote hook by calling it like a normal hook.

export default function App() {
	const quote = useQuote()

	return (
		<View style={styles.container}>
			{quote && (
				<React.Fragment>
					<Text>{quote.text}</Text>
					<Text>{quote.author}</Text>
				</React.Fragment>
			)}
		</View>
	)
}

Because fetching data from server is asynchronous, we need to check wether our quote is fetched or not. In JavaScript, we can wrap our component with && expression. So that our component won't be rendered whenever the expression returns false.

Finally, we display our text and author object property with the Text component provided by the react-native.

To make our app look cleaner, we can define some styles to our quote text using React Native StyleSheet.

export default function App() {
	const quote = useQuote()

	return (
		<View style={styles.container}>
			{quote && (
				<React.Fragment>
					<Text style={styles.quoteText}>{quote.text}</Text>
					<Text style={styles.quoteAuthor}>{quote.author}</Text>
				</React.Fragment>
			)}
		</View>
	)
}

// New
const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: "#fff",
		alignItems: "center",
		justifyContent: "center",
	},
	quoteText: {
		fontSize: 24,
		textAlign: "center",
	},
	quoteAuthor: {
		marginTop: 15,
		fontSize: 18,
		color: "#616161",
	},
})

So far so good, now we need to fetch our quotes from our server inside useEffect hook.

function useQuote() {
	const [quote, setQuote] = React.useState(null)

	React.useEffect(() => {
		// Fetch data here
	}, [])

	return quote
}

Here, we use useEffect hook to let us run a function when our component is loaded to the screen. In addition, we also can use this hook to do something when some state has changed.

If you wonder why the second parameter of the useEffect hook is an empty array. That's because we don't want to listen to any state changes inside our custom hook. To find our more about useEffect hook, checkout this post.

Inside this useEffect, we can shoot a request to our server with fetch method.

React.useEffect(() => {
	fetch("http://localhost:3000/quotes")
		.then((response) => response.json())
		.then((quotes) => {
			const randomIndex = Math.floor(Math.random() * (quotes.length - 1))
			setQuote(quotes[randomIndex])
		})
}, [])

The randomIndex constant generates a random number from 0 to the maximum index of quotes array. We use this number to get a random quote and update our quote state with setQuote function.

Random Quote Button

Now, our app is almost complete. The last thing to do is to add a button that will generate a new random quotes from the server when it pressed. So, we need a function to generate random quote from the server outside the useEffect hook. We can add this functionality by refactoring our custom hook a little bit.

function useQuote() {
	const [quote, setQuote] = React.useState(null)

	React.useEffect(() => {
		updateQuote() // New
	}, [])

	// New
	function updateQuote() {
		fetch("http://localhost:3000/quotes")
			.then((response) => response.json())
			.then((quotes) => {
				const randomIndex = Math.floor(Math.random() * quotes.length)
				setQuote(quotes[randomIndex])
			})
	}

	return { quote, updateQuote }
}

Here, we move the useEffect hook callback content to a new function called updateQuote, which used to fetch a random quote from our server. So that, we can return our quote state and the updateQuote function to our component.

With this changes, we need to adapt our App component. We can define a Button component, provided by the react-native package, to generate new random quote.

export default function App() {
	const { quote, updateQuote } = useQuote() // Update

	return (
		<View style={styles.container}>
			{quote && (
				<React.Fragment>
					<Text style={styles.quoteText}>{quote.text}</Text>
					<Text style={styles.quoteAuthor}>{quote.author}</Text>
					<Button onPress={updateQuote} title="Show Me Another Quote!" /> // New
				</React.Fragment>
			)}
		</View>
	)
}

Wrap Up

Our app is now completed!

To summarize this, here is the full code of our React Native quotes app in just 55 lines of JavaScript code!

import React, { useState, useEffect, Fragment } from "react"
import { StyleSheet, View, Text, Button } from "react-native"

function useQuote() {
	const [quote, setQuote] = useState(null)

	useEffect(() => {
		updateQuote()
	}, [])

	function updateQuote() {
		fetch("http://localhost:3000/quotes")
			.then((response) => response.json())
			.then((quotes) => {
				const randomIndex = Math.floor(Math.random() * quotes.length)
				setQuote(quotes[randomIndex])
			})
	}

	return { quote, updateQuote }
}

export default function App() {
	const { quote, updateQuote } = useQuote()

	return (
		<View style={styles.container}>
			{quote && (
				<Fragment>
					<Text style={styles.quoteText}>{quote.text}</Text>
					<Text style={styles.quoteAuthor}>{quote.author}</Text>
					<Button onPress={updateQuote} title="Show Me Another Quote!" />
				</Fragment>
			)}
		</View>
	)
}

const styles = StyleSheet.create({
	container: {
		flex: 1,
		backgroundColor: "#fff",
		alignItems: "center",
		justifyContent: "center",
		padding: 25,
	},
	quoteText: {
		textAlign: "center",
		fontSize: 28,
	},
	quoteAuthor: {
		fontSize: 18,
		marginTop: 25,
	},
})
Profile picture

Abdurrahman Fadhil

I'm a software engineer specialized in iOS and full-stack web development. If you have a project in mind, feel free to contact me and start the conversation.