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 Promisespromise 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 resvar. Then, using the json()method we can pass that along as a JavaScript object that’s assigned to the datavar. 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 textI 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 in](http://r.in) 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

More about:

Mickey Martinez

Mickey is a Software Engineer at Echobind, with a knack for problem solving with innovative solutions. A former bootcamp grad, he’s developed an avid interest in teaching, mentoring, and advocating for those just starting in software development. As a React.js to mobile convert, he primarily utilizes React Native as his paintbrush to go full Bob Ross on an empty canvas. Beyond coding, Mickey enjoys studying ancient history, world travel, tacos de asada, and bumpin’ some G-funk classics down the PCH.