In the world of web development, sometimes it's fun to add a bit of personality and levity to your site. This post will explore how I added a simple "Random Programming Joke" card to my portfolio website using React and the Joke API. This seemingly minor feature involves concepts like API consumption, state management in React, and handling asynchronous operations.
JokeCard.tsx
The heart of this feature lies within the components/JokeCard.tsx
file:
import React, { useEffect, useState } from "react";
const JokeCard = () => {
const [joke, setJoke] = useState<string | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchJoke = async () => {
try {
const response = await fetch(
"https://v2.jokeapi.dev/joke/Programming?format=json"
);
if (!response.ok) {
throw new Error("Failed to fetch joke");
}
const data = await response.json();
setJoke(data.joke || `${data.setup} - ${data.delivery}`);
setError(null);
} catch (error) {
console.error("Error fetching joke:", error);
setJoke(null);
setError("Oops! Something went wrong. Please try again.");
}
};
fetchJoke();
}, []);
return (
<article className="rounded-xl border-2 border-gray-100 bg-white dark:border-gray-800 dark:bg-gray-900">
<div className="flex items-start gap-4 p-4 sm:p-6 lg:p-8">
<div>
<h3 className="font-medium sm:text-lg text-gray-900 dark:text-gray-100">
<a href="https://jokeapi.dev/" className="hover:underline">
Random Programming Joke
</a>
</h3>
<p className="text-sm text-gray-700 dark:text-gray-300">
{error ? error : joke || "Loading joke..."}
</p>
<div className="mt-2 sm:flex sm:items-center sm:gap-2">
<p className="hidden sm:block sm:text-xs text-gray-500 dark:text-gray-400">
Joke API
</p>
</div>
</div>
</div>
</article>
);
};
export default JokeCard;
Let's break this down step-by-step:
Import Statements
import React, { useEffect, useState } from 'react';
useEffect
(for side effects like API calls) and useState
(for managing the component's state)Component Definition
const JokeCard = () => { ... }
JokeCard
State Management
const [joke, setJoke] = useState<string | null>(null);
joke
state variable will hold the joke text as a string. We initialize it to null
to indicate that we don't have a joke yet.const [error, setError] = useState<string | null>(null);
error
state variable holds error message, set to null
initially.useEffect
Hook
useEffect(() => { ... }, []);
useEffect
hook is used here to trigger the API call once after the component mounts ([]
empty dependency array).fetchJoke
Function
const fetchJoke = async () => { ... }
Fetching Data: const response = await fetch('https://v2.jokeapi.dev/joke/Programming?format=json');
fetch
to make a GET
request to the Joke API. The URL here is specifically requesting a "Programming" category joke in JSON format.Error Handling:
if (!response.ok) { throw new Error('Failed to fetch joke'); }
response.ok
. If not, we throw a new error, which will jump to the catch
block.Parsing JSON: const data = await response.json();
Setting State:
setJoke(data.joke || `${data.setup} - ${data.delivery}`);
joke
string, or with a setup
and delivery
. If data.joke
exists, we use that; otherwise, we construct the joke string from setup and delivery.setError(null)
: if the API call is successful, the value of the error state will be null. URL), we set the joke
state to null
and update the error
state.
JSX Rendering
Initial Render: When the component mounts, the useEffect
hook triggers the fetchJoke
function to get data from the API
Data Fetching: The fetch method is used to send a request to the specified URL and gets data from the API.
State Updates: The fetched data is used to update the state variable, causing the component to re-render and display the joke.
Error Handling: If anything goes wrong, the error
state gets updated which will display the error message, such as "Failed to fetch joke" or "Something went wrong. Please try again"
UI Display: The UI will then display the joke inside a card component or a "Loading..." message while the data is being fetched.
Asynchronous Operations: API calls are asynchronous, meaning they take time to complete. We use async/await
to handle them elegantly.
useState
Hook: This hook is fundamental for creating interactive interfaces, enabling the component to update and re-render as the joke data changes.
useEffect
Hook: useEffect
helps us perform side-effects after render such as API calls. The empty array in dependency makes sure that it is called only once.
Error Handling: It's essential to handle potential errors when fetching data from external APIs, ensuring a better user experience.
UI: The UI is responsive so that the user can easily view the Joke Card.
To integrate this component into your homepage, you would simply import and use it within your main page component (pages/index.tsx
):
import JokeCard from "components/JokeCard";
export default function Home() {
return (
<div>
<JokeCard />
</div>
);
}
Fetching and displaying data from an API might seem complex, but it can be broken down into smaller, more understandable parts, as we've seen here. This approach demonstrates how to handle asynchronous operations and state updates in a React component, all while adding a small piece of fun to the website. The JokeCard
component makes use of the Joke API and provides a light-hearted addition to the website, while also showcasing real-world application of React and API handling techniques.
This structure provides a foundation for handling other dynamic content on your website, allowing you to connect to all types of APIs and create compelling user experiences!