Author: Frank Anderson
Last Updated: 31 December, 2022
We will be making a Profile Generator to generate Profile Cards for Oasis members. Below is a screenshot of what we’ll be building. Users enter information in the blue section and then click Generate!
. Upon clicking, the green section appears. This tutorial won’t cover every step, but the sample code is available below too so you can follow along there.
Sample code to follow along. You will need to add this src
folder to your own create-react-app.
Sample code to follow along. You will need to add this src
folder to your own create-react-app.
First, launch VS Code, setup your repo, and run create-react-app
. See Installing Visual Studio Code, Version Control, and/or React Basics (Setup, State, & Props) if you need some help with this.
Now that we’re ready to code, let’s start by blocking out our main components. From the example, we see there are three clear sections: the header, input form, and output Let’s reflect this in code:
// App.js
import Header from "./Header"; // import each of your 3 components
import Form from "./Form";
import Output from "./Output";
export default function App() {
return (
<div>
<Header />
<Form />
<Output />
</div>
);
}
// Header.js
export default function Header() { /* ... */ }
// ...
While we’re at it, let’s think about which components need access to which properties. We need four state variables to track values for each input, and one to decide if the output should be shown.
// App.js
import { useState } from "react"; // required to use state
import Header from "./Header"; // import each component
import Form from "./Form";
import Output from "./Output";
export default function App() {
// input field values
const [photo, setPhoto] = useState(null);
const [name, setName] = useState("");
const [title, setTitle] = useState("");
const [fact, setFact] = useState("");
return (
<div>
<Header />
<Form />
<Output />
</div>
);
}
Not every variable needs to be passed to every component. The Form
needs everything, but the Output
only needs photo
, name
, title
, and fact
, and the Header
doesn’t need anything. It is important to only provide each component the data it actually needs.
// App.js
import { useState } from "react"; // required to use state
import Header from "./Header"; // import each component
import Form from "./Form";
import Output from "./Output";
export default function App() {
// input field values
const [photo, setPhoto] = useState(null);
const [name, setName] = useState("");
const [title, setTitle] = useState("");
const [fact, setFact] = useState("");
// whether to show the output
const [showOutput, setShowOutput] = useState(false);
return (
<div>
<Header />
<Form
photo={photo} setPhoto={setPhoto}
name={name} setName={setName}
title={title} setTitle={setTitle}
fact={fact} setFact={setFact}
showOutput={showOutput} setShowOutput={setShowOutput}
/>
<Output photo={photo} name={name} title={title} fact={fact} />
</div>
);
}
The header is a quick component to set up. It contains only h1
and p
elements:
export default function Header() {
return (
<>
<h1>Profile Generator</h1>
<p>Generate profile cards for Oasis members.</p>
</>
);
}
To create the form, we need to tell React what to do when the form is submitted. This can be accomplished by accessing the onSubmit
event. We also need to tell React what to do when each input is changed so that we can keep track of its value. We do this by accessing the event
property in the onChange
event.
Let’s create a form using HTML and then access these onChange
and onSubmit
events using React:
// in Form.js
// function header:
export default function Form({ /*...*/ name, setName /*...*/ }) {
// in return ...
<form
onSubmit={(e) => { // on submission ...
e.preventDefault(); // stop submit from reloading the page
setShowOutput(true); // display the output,
// using the setShowOutput() state setter passed into this Form component
}}
>
<label htmlFor="member-name">Name: </label> { /* htmlFor connects the label the appropriate input field */ }
<input
id="member-name" // match id to label's htmlFor
type="text" // type of HTML input (text, email, phone, etc.) - see <https://www.w3schools.com/html/html_form_input_types.asp>
value={name} // display value of the provided `name` state prop
onChange={(e) => { // access the onChange event handler; `e` is short for event but can be named anything */
setName(e.target.value); // get the value of the event (text in the input field)
}}
required // mark this HTML field as required
/>
{/* continue for rest of inputs */}
</form>
Continue building this out for the rest of the input fields you need. One input which will prove challenging is the photo because we need to turn the URL into a photo we can display. We do this by accessing the file’s path from the event, creating a URL from it, and then by getting an object at that URL inside of your onChange
:
<input
id="member-photo"
type="file"
// no `value` because value is a path, but we want to store a photo
onChange={(event) => {
setPhoto(URL.createObjectURL(event.target.files[0]));
}}
required
/>
Once this is all complete, it’s time to move on to the output section.
For the output, we need to display an image and text using the passed in properties:
export default function Output({ photo, name, title, fact }) {
return (
<div>
<img src={photo ?? ""} alt="profile photo" />
<h1>{name}</h1>
<p>{title}</p>
<p>Fun Fact:{fact}</p>
</div>
);
}
<aside>
💡 Above, photo ?? ""
evaluates to ""
if photo
is a nullish value (null
or undefined
). This is called the Nullish Coalescing operator.
</aside>