Author: Frank Anderson

Last Updated: 31 December, 2022

📊 Table of Contents

🥅 The Goal

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.

Untitled

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.

📦 Setup

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>
  );
}

🔠 Header

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>
    </>
  );
}

📝 Form

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.

🏞️ Output

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>