Building a Flashcards App
Brainstorming
I want to build a flashcard app.
MVP
I'm imagining a dataset of questions/answers and I can display each question as a "card". When I click on the card, it will "flip" and show me the answer.
Stretch features
I don't have as clear of a picture of how I will implement these details so when planning, I'll list these features as user stories:
Users can keep score
There is a form so that users can add new flashcards
Users can make "playlists" or "quizzes" that show a specific set of flashcards
Setup
First, create the app using Vite: npm create vite@latest (you may be prompted to install the latest version of vite)
npm create vite@latest
# Name it flashcards
# Select React
# Select JavaScript
cd flashcards
npm i
# Delete the contents of App.jsxData
I know that I'll need some flashcard data to render.
To get myself started, I used ChatGPT to give me a dataset of flashcards rather than come up with my own set of questions. I asked for the data in JSON format so that I could easily import it into my app.

I then stored the resulting data in a .json file called src/db/flashcards.json (I made a src/db folder since this is sort of like my "database").
{
"flashcards": [
{
"id": 1,
"question": "What is React?",
"answer": "React is a JavaScript library for building user interfaces."
},
{
"id": 2,
"question": "What is JSX?",
"answer": "JSX is a syntax extension for JavaScript used with React to describe what the UI should look like."
},
{
"id": 3,
"question": "What are components in React?",
"answer": "Components are the building blocks of a React application. They encapsulate logic and UI."
},
...
]
}ChatGPT did a great job of giving me data in a format that I could easily use.
The data was in an Array which means I can render a "card" for each object using
.map()in aulEach flashcard object had:
an
idwhich I can use for list itemkeyprops and much morea
questionand ananswerwhich will be useful for when I want to toggle which text I show to the user.
JSON Server
JSON Server is a tool to we use to spin up a mock API. It basically lets us turn any properly formatted .json file into a full API running on localhost.
It is a great alternative when you don't have the time to build out a full Express API. It does have its limitation in that it cannot support a robust relationships database. Read the JSON Server documentation for more information.
Using the JSON file we created above, we can create a mock API. To set it up we can:
Run
npm install -g json-serverto install json server globallyCreate the
.jsonfile. We did this already:db/flashcards.jsonFrom the root of your vite project, split your terminal and run
json-server --watch db/flashcards.json --port 4000to start a mock back-end server on port 4000.

Now, you will have an API that you can access via the URL http://localhost:4000/flashcards (try visiting that URL in your browser!)
json-server only works if the .json file is in the proper format. The JSON file needs to store a JSON object with a top-level property that names the resource to be fetched.
Something like (feel free to copy this):
{
"flashcards": [
{
"id": 1,
"question": "What is React?",
"answer": "React is a JavaScript library for building user interfaces."
},
{
"id": 2,
"question": "What is JSX?",
"answer": "JSX is a syntax extension for JavaScript used with React to describe what the UI should look like."
},
{
"id": 3,
"question": "What are components in React?",
"answer": "Components are the building blocks of a React application. They encapsulate logic and UI."
}
]
}In this example, "flashcards" is the top-level property which makes http://localhost:4000/flashcards a valid endpoint. When we send a GET request to that endpoint, we'll get back the value of "flashcards".
Q: What would be the endpoint(s) created if this were our JSON file?
{
"friends": [
"ben",
"gonzalo",
"carmen"
],
"message": {
"data": "hello world"
}
}Q: How would I make a http://localhost:4000/flashcards/react or http://localhost:4000/flashcards/fetch endpoint?
Component Structure
To make the MVP, the app can be quite simple. Just render a ul with an li "card" for each flashcard object. So I basically just need my App component and Flashcard component. I'll then map each object in the dataset to a <Flashcard />.
For the MVP, here is what I came up with:
Flashcard Component
The Flashcard component should be focused solely on rendering a single flashcard object. It can maintain its own state to toggle back and forth between showing the question and the answer.
import { useState } from 'react'
const Flashcard = ({ flashcard }) => {
const [text, setText] = useState(flashcard.question)
const [backgroundColor, setBackgroundColor] = useState('lightblue')
const flipCard = () => {
if (text === flashcard.question) { // show the answer
setText(flashcard.answer);
setBackgroundColor('lightgreen');
} else {
setText(flashcard.question); // show the question
setBackgroundColor('lightblue');
}
}
// set the style dynamically using the backgroundColor state
const style = { background: backgroundColor }
return (
<li className="card" onClick={flipCard} style={style}>
<p>{text}</p>
</li>
)
}The
Flashcardcomponent takes in aflashcardobject as a prop.It also keeps track of two state values:
textandbackgroundColorwhich can be toggled between showing the question and showing the answerWe provide a
styleprop to dynamically set the style of the component using thebackgroundColorstateWe render the flashcard as an
liwith anonClickprop, astyleprop, and with thetextstate rendered.
App Component
The App component needs to fetch the set of flashcards from the json-server URL http://localhost:4000/flashcards when the component first loads and then use that data to render a list of flashcards.
import './App.css'
import { useState, useEffect } from 'react'
import fetchData from './utils/fetchData'
// Check out the helper function ^
const Flashcard = () => {
// flashcard component
}
function App() {
const [flashcards, setFlashcards] = useState([]);
const [error, setError] = useState(null);
useEffect(() => {
const doFetch = async () => {
const [data, error] = await fetchData('http://localhost:4000/flashcards');
if (data) setFlashcards(data);
if (error) setError(error);
};
doFetch();
}, []); // run the effect only once
// Conditionally render the error message
if (error) return <p>{error.message}. Refresh to try again.</p>
return (
<>
<h1>Flash Cards</h1>
<ul>
{
flashcards.map((flashcard) => <Flashcard key={flashcard.id} flashcard={flashcard} />)
}
</ul>
</>
)
}
export default AppLet's break it down:
The
Appkeeps track offlashcardsanderrorstate.We use
useEffectto fetch the flashcard data from our json-server when the component first renders (and only that one time).Then we either invoke
setFlashcardsorsetErrordepending on the returned data.
The
Appcomponent maps over theflashcardsdata, creating aFlashcardcomponent for eachflashcardobject.When rendering a list of components, we use the
flashcardobject'sidas thekeyand pass along theflashcardobject as a prop.Since I kept the starting CSS styles that came with the Vite project, it actually looks okay.
Next Steps
Organize my components into separate files
Build out stretch features
Add better styling
Last updated