Custom React Hook Guide: Build a Kanye Quote app with React Native
Today’s objective is to reveal the closely held secrets around creating custom React Hooks with React. Only a chosen few have been gifted this knowledge, so if you’re ready to elevate your game ride with me to the finish line.
We’ll accomplish this with the help of Mr. West, the Yeezus of knowledge, by building a React Native app that hits a public Kanye Quotes API and returns data through a custom React Hook. Specifically, we’ll leverage the useEffect
and useState
Hooks, two of the most commonly used hooks in the React Hooks API.
Hooks are functions that let you “hook into” React state and lifecycle features from function components. Hooks don’t work inside classes — they let you use React without classes.
If you’re new to React, the cool thing is that Hooks are the exact same in React Native and React.js. With Hooks, we no longer need to write class components to manage state or query data — eliminating pesky lifecycle method bugs like componentDidMount
, componentDidUpdate
and componentWillUnmount
. Instead this guide will pair functional components with React Hooks, demonstrating how to reuse stateful logic and manage side effects across components.
Since the concepts of Hooks are platform agnostic, I’ve chosen to crank out a fully functional RN mobile app — focusing on iOS
.
Enough of the abstract talk, buckle up cuz we about to ball so hard .. ya you know the rest!
Getting started
API
The API will be serving up a mix of certified Kanye West quotes, and I’ll be sharing a few of my favorite verses too. By the end you’ll have gone from College Dropout to the Graduation 🚀🐻.
🎵”Ain't nobody expect Kanye to end up on top
They expected that College Dropout to drop and then flop” - *Last Call, The College Dropout*
Visiting the Kanye West API in your browser, you’ll see it returns a very simple JSON response of a randomized Kanye quote. Refresh and a new quote is displayed. Many times APIs will return a much larger JSON response, for example also including the album name, song title, release date, etc. For today, a simpler response will allow us to practice mastering the craft.
Viewing a quote from the Kanye West API endpoint in browser
React Native Expo
Build
I’ll be going with the Expo CLI Quickstart option to create a new React Native project.
💡Tip: If you’re new to React Native, check out the Setting up the dev environment docs. I recommend beginners starting with the Expo CLI route vs. React Native CLI.
Open your terminal of choice, make sure Node is installed, and then install the Expo CLI command line utility tool using npm
or yarn
.
With Expo CLI installed, create a new React Native project named KanyeQuoteApp
by running the following commands in your terminal:
# Creates project named KanyeQuoteApp expo init KanyeQuoteApp
Choose the blank
template and hit enter
. Expo will do the rest from here!
Now switch into the KanyeQuoteApp
directory, open VS Code, and start the development server:
# Navigate to project directory cd KanyeQuoteApp # Open project in VS Code code . # Start server. Equivalent to: expo start yarn start
Viewing the default KanyeQuoteApp project in VS Code
Since React Native projects are for mobile devices, you’ll need to open an iOS simulator. If you develop on Android, launch an emulator with Android Studio. To open an iOS simulator with Expo Go, press i
in the terminal, or use the dev tools in the browser and click Run on iOS simulator
.
Expo Go CLI tools in terminal
💡Tip: Make sure Xcode and Expo Go are installed. View the Expo docs on setting up iOS simulator and Expo Go app.
Default iOS app on iPhone 12
Once launched, the default iOS app will look similar to the image above. A blank white canvas for us to go full Pablo Picasso on. So let’s pop in the Kanye West Get Right for the Summer workout tape, and really switch the style up!
Grab the stylized template from my GitHub repo, replace everything in App.js
and Save. Expo Go should automatically reload the app whenever a file is changed. If not, try command + r
on device, or r
in terminal. Commit 1
Updated app with stylized template
Ayyy that looks like the vibe I’m going for, now it feels like we on an ultralight beam!
Building out the useEffect
With styling complete, let’s focus on getting the useEffect
Hook built out and functional in App.js
. Once it’s working as intended, we’ll then move that hook to its own file where it will be remixed into a custom hook — therefore accessible and reusable by our entire app.
In App.js
import useEffect
from React:
import React, { useEffect } from "react";
Now add an empty useEffect()
above the return
block:
The useEffect
hook takes a callback function as it’s first argument, and an optional dependency array as the second argument.
useEffect( callback, dependency ) // dependency is an array
In our case, we’re starting with no dependencies, so your gut may be saying “we don’t need the optional dependency array.” While true, excluding the dependency array causes the effect to run with every render, potentially creating unforeseen performance issues like an infinite loop*.* By adding an empty dependency array []
, we’re letting React know to run this effect only when the component mounts and unmounts.
Within the callback function, create an async function named fetchKanyeQuote
that awaits the response data from a fetch
call to the Kanye API. More details on async
/await
here. Be careful to not make the callback function itself async, as that will cause issues with data calls being out of sync. Instead target your inner functions within the effect’s callback.
useEffect(() => { const fetchKanyeQuote = async () => { await fetch("https://api.kanye.rest/"); }; }, []);
Previously, we’d opt to chain together a few Promises — promise chaining — to make use of the API data. You can take a look at that approach here.
A more modern approach is to use try/catch
blocks instead, assigning the responses to variables we can easily reuse. Below, fetch()
returns a promise containing a JSON response that is assigned to the res
var. Then, using the json()
method we can pass that along as a JavaScript object that’s assigned to the data
var. Check it out below:
// useEffect with a modern try/catch block useEffect(() => { const fetchKanyeQuote = async () => { try { const res = await fetch("https://api.kanye.rest/"); const data = await res.json(); console.log(data); } catch (error) { console.error(error); } }; fetchKanyeQuote(); }, []); // Promise chaining approach for comparison useEffect(() => { const fetchKanyeQuote = async () => await fetch("https://api.kanye.rest/") .then((res) => res.json()) .then((data) => { console.log(data); }) .catch((error) => { console.error(error); }); fetchKanyeQuote(); }, []);
To confirm the Promise was successful, you can console.log
the data
var and see the structure of the response. Remember to call the fetchKanyeQuote()
function at the end of the useEffect as seen above.
In the terminal we should see the simple structure of the data
object: A quote
property that has a radomized Kanye quote. To access and display the quote, we’ll need to tap into data.quote
but first we need a way to save this quote in memory. Commit 2
🎵”How you say broke in Spanish? Me, no hablo” - Dark Fantasy, M.B.D.T.F
Store Data Response in State (useState
)
So how do we store this quote (data
) in memory — state in React — and display it on screen?
Well Sway, this is where the useState
hook shines bright like a diamond 💎💎
Import useState
from React and then we need to initialize state before the useEffect
. The standard useState
hook is structured like so:
const [stateVar, setStateVar] = useState(initialState);
In our case, the initialState
of the stateVar
variable should be set to an empty string ""
. Then we’ll use the setter function setStateVar
to update the initialState
value with an actual quote from the API.
💡Tip: With the useState
hook, you never want to alter stateVar
directly, instead always use the setter function. Doing so will not trigger a re-render.
Since we can name stateVar
anything we’d like, let’s go with quote
. And setStateVar
will be setQuote
.
// Assigning initial value of quote var to an empty string const [quote, setQuote] = useState("")
Now we can replace console.log(data)
with our setter function setQuote(data.quote)
. This is the proper way to update the quote
variable when using the useState
hook.
import React, { useEffect, useState } from "react"; export default function App() { const [quote, setQuote] = useState("") // useState structure useEffect(() => { const fetchKanyeQuote = async () => { try { const res = await fetch("https://api.kanye.rest/"); const data = await res.json(); setQuote(data.quote); // setter function updates initialState value } catch (error) { console.error(error); } }; fetchKanyeQuote(); }, []);
At this point, we’ve made an async call to the API requesting a Kanye West quote and awaited the response. We then converted that to an object, and passed that value to our setter function which updates the quote
var. Only thing left to do is display the quote on device.
In the return
block, within the <Text>
component replace the hard coded text “I Love You Like Kanye Loves Kanye” with the quote
variable and Save. The quote
variable needs to be wrapped in curly braces {}
.
Updating hard coded text to display an actual Kanye quote from the API via useState
Lamborghini Mercy, your device should now be lookin’ fly to death and serving up fresh snippets of Ye.
🎵“Let the suicide doors up I threw suicides on the tour bus I threw suicides on the private jet You know what that mean, I'm fly to death” - Mercy, Good Music - Cruel Summer
This confirms both of our hooks are doing exactly what we want, fetching data and storing that data in state.
For the moment, to fetch a new quote you’ll need to reload the app by pressing r
the terminal. As a bonus I’ll eventually make the More Kanye
button functional, so that it’ll fetch a new quote when pressed.
Don’t runaway now, we’re on to the good part! Commit 3]
Custom Hook 🪝
Here’s where we take our working logic and remix it into a custom hook.
A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks
Create a new file named useRandomQuote.js
— the file naming here intentionally starts with use
to signify that it’s custom hook.
Scaffold out a functional component named useRandomQuote
and import useEffect
and useState
:
// useRandomQuote.js import { useEffect, useState } from "react"; const useRandomQuote = () => { }; export default useRandomQuote;
Reopen App.js
and cut out the entire useEffect
block, as well as the line of code we need to manage state [quote, setQuote]
. Paste this block of code within the body of the useRandomQuote
function.
Be sure to return the quote
variable within useRandomQuote so that we can make its present value available wherever the custom hook is used.
// useRandomQuote.js import { useEffect, useState } from "react"; const useRandomQuote = () => { const [quote, setQuote] = useState(""); // useState structure useEffect(() => { const fetchKanyeQuote = async () => { try { const res = await fetch("https://api.kanye.rest/"); const data = await res.json(); setQuote(data.quote); // setter function to update initial state val } catch (error) { console.error(error); } }; fetchKanyeQuote(); }, []); return quote; // remember to add this line }; export default useRandomQuote;
Reopen App.js
, import the custom hook, and assign it to a variable named kanyeQuote
.
import useRandomQuote from "./useRandomQuote";
Assigning the custom hook to kanyeQuote var
Finally, update the variable from quote
to kanyeQuote
and Save. If a new quote renders like before, you’ve successfully implemented a working custom hook! Commit 4
💡 If the quote is blank, double check that you returned the quote
variable within useRandomQuote()
.
Using our custom hook to display a new Kanye quote
That’s all it takes to share logic between components with React Hooks. As your app grows, the custom hook can be used again and again!
We’ve covered how a useEffect
helps us grab API data, store that data locally with useState
, and then house all that logic in a custom hook. I hope you’re feeling like a Monster at this point! I know I’m definitely in my zone.
Beyond custom hooks knowledge, you also have a modern React Native build — for some of you a first! [Completed project]
If you’re thinking, Nah Nah Nah he never showed us how to make the button work, I got you. Hit the Bonus section below. And if this is the end, *take it Eazy*!
🎵”The Lyor Cohen or Dior Homme, that's Dior Homme not Dior homie” - Devil in a New Dress, M.B.D.T.F
Bonus: Fixing the Button
with onPress
At the moment, a new quote requires restarting the app. This would be terrible UX in an app, which is why I wanted to break down the solution. The fix requires something to trigger the useEffect
hook to refetch a new Kanye quote. The new quote is then passed back to the kanyeQuote
variable and finally updated on device.
We can tap into the onPress
prop in <TouchableOpacity>
, and pass through a function that is triggered when the button is pressed. This function needs to tell the custom hook that we want a new quote. There are a few ways to make this happen.
In my approach, I’ll be passing a parameter to the useRandomQuote
hook, adding the param to dependency array, which will trigger the hook to run again if the param value changes.
In App.js
let’s set up an iterator with useState
and then pass the setter function to onPress
to update the value of i
.
// Iterator to trigger custom hook const [i, setI] = useState(0);
Locate the onPress
prop and add the setI
setter function.
// Update onPress prop with setter function <TouchableOpacity style={styles.button} onPress={() => setI(i + 1)}>
Now add the i
parameter to the custom hook in order to re-fire the hook if the value changes — this will happen when the button is pressed, as the value of i
will change from 0
to 1
.
const kanyeQuote = useRandomQuote(i); // add i as a parameter
Lastly, open up useRandomQuote.js
and receive the i
param, then pass it to the dependency array. Save these changes and test out the More Kanye
button. Your button should be serving up fresh quotes each time it’s pressed! Commit 5
Receiving i param and adding to useEffect dependency array
That’s a wrap on the bonus content. You’ve come along way with Ye! If you’re a fan, here’s Yandhi, an unreleased project of his. Keep grinding and soon you’ll be a **Champion of hooks**.
🎵 “So drive slow homie, you never know homie ... you need to pump your brakes and drive slow, homie” - Drive Slow, Late Registration