What You Will Learn
In this developer guide, you will learn how to:
- Create a basic React app
- Create a component with a user registration form to enable your app's user register on your site
- Add event listeners and event handlers to properly detect form input and process the form data in your React component
- Make Calls over HTTP to an API server to submit the form data as JSON
- Add some simple styling to your component to make your UI more user-friendly
1: Intro
Before we start on this journey, let's decide on what we are going to do and how...
1.1: What we are Going to Build
Throughout this guide, we will work through building a simple React app that enables a user to fill out a form to create an account on the website.
There won't be much complex logic (since the app will simply be demonstrative) but we will take a look at a few connected things like listening for and handling events on the form, validating form data and making an HTTP call to an API server to submit the form data to the backend server.
We will call our React app `user-base` and the form-handling component `Register`.
In the following sections, we will delve into the implementation details. So, pull out your notebook (and code editor) and follow along as much as you can.
1.2: Tools we are Going to Use
To prevent a case where we have to build everything - component markup, form styling, API logic, form input validation, etc.- from scratch, we will reuse a few (NPM) packages to be more productive and "ship" our web app faster.
The tools we are going to use will include:
- useState() Hook: This built-in React hook is what we wil use to set and manage the component's local state.
- react-bootstrap: We will use this NPM package to style our component and its registration form.
- Fetch API: This is a global JavaScript function (i.e. `fetch()`) that we can use instead of the raw `XMLHttpRequest` to make CRUD calls over HTTP to an API server.
- `json-server`: Fake REST API Server: Since we are going to make API calls to submit our component's form data, we will (very obviously) need an API server to take those calls. But since the app we will be building in this guide is so simple, we don't want to invest too much time and energy into building a backend API server.
So, instead, I have taken the liberty to spin up a fake/test REST API server using the
json-server package. For details on how to create a FAKE REST API server to test with while developing your frontend (React) apps, please, see the
json-server documentation.
1.3: To Develop this App
As a web developer, very rarely will you just build an app with just static text, audio or video.
Usually, your web app will need to support taking user-contributed data using forms on the web app. These user data could be text, images, audio, video or of other formats.
So, in this guide, we will focus on developing a simple React app that will contain a component with a form that enables the user to register for an account on the website. To look a bit professional, we will use the
`react-bootstrap` styled component package to style the component and its form. Finally, we will use the
`Fetch API` to make calls to a remote API server to submit the form data.
So, strap yourself in tightly and comfortably and let's drive this lesson home ...
2: Create the App
First, we will install React and create the skeletons of our app.
We will just summarize how to do that here. If you are not familiar with React and/or need to revise, please, see the
"Let's Create Our First React App" guide for details on how to properly start and structure a React app. Then, come back here to learn how to handle form processing in your React app's components.
2.1: Install React
Before you move on, make sure that you have both
NodeJS and
NPM installed on your machine.
Next, head over to your command line/terminal and install React with:
npm install create-react-app
Now, start a new React app using:
create-react-app user-base
This will create an app named `user-base` in the current directory of your terminal.
You have laid the "foundational stones" for building your React app.
2.2: Start the App
Now, let's see how your app looks. Too early for that? No problem. It doesn't hurt to look. :)
In the terminal, run:
npm run start
This will start the React development server.
By default, your React app will run on port 3000. So, open your web browser to `http://localhost:3000`.
You will notice that your React app just renders the React logo and a link to the official docs.
This is useful in one major way: it shows that you have successfully-installed React and some of its NPM dependencies (i.e. the NPM packages that React needs to run). So, congratulations on a bug-free start! You did it! :)
Our job is to replace what's currently rendered on the page with our own components' UI and functionality.
So, let's start doing that.
2.3: Create the App Folder
Most of your app-building work will happen in the `src/` folder of your app. The `src/App.js` file contains the code (and, possibly, markup) for your app's root `App` component. That's where your routes and app-wide state will go. But we won't be covering either of that here.
So, let's lay the groundwork for our app.
`cd` into the `user-base/src/` folder and let's create some folders to hold different parts of our React app.
- `components/`: This folder will hold all the files for our various custom React components. Create this folder with the `mkdir components` command.
- `apis/`: This will hold the files for our API classes. Create this with `mkdir apis`
Since ours is a simple app, we will stop here and move on to the business of building the app.
2.4: Create the Components
`cd` into the `src/components/` folder and create some components. Usually, you can create some app-wide/general components (that are used alongside every other component of your app). These would include the `Header` and `Footer` components: to contain the header and footer section of your web app.
Next, we will create the "star" component of this guide: `Register`. This will contain the form for our visitors to register for accounts on the website.
So, use `touch Register.js` to create the file for the `Register` component.
Then, let's develop the basic UI of the `Register` component, like this:
import React from 'react'
const Register = () => {
return (
<div>
<h3> Register for an Account</h3>
<p>
Fill out the form below to register for an account on the UserBase platform.
</p>
<form>
<label> First Name</label>
<input
type="text"
placeholder="Enter your first name"
/>
<label> Last Name</label>
<input
type="text"
placeholder='Your Last Name'
/>
<label> Your Email </label>
<input
type="email"
placeholder="Enter a valid Email"
/>
<input
type="password"
placeholder="Password with both numbers and letters"
/>
<input
type="submit"
value="Register"
/>
</form>
</div>
)
}
export default Register
Next, create a route for this component so that we can load and view it in the browser.
Open the `src/App.js` file and add a route for the `Register` component like this:
<Route path='register' element={<Register />} />
Of course, I am assuming you are using the
react-router-dom package and have set it up in the root `App.js` file to handle routing for your React components. If you use a different routing package, please, see its docs for how to properly configure routes for your React app's components.
Now, load the `Register` component in the browser by visiting `http://localhost:3000/register`. You should see a very basic form with absolutely no styles whatsoever. Looks ugly? We will fix that later. But for now, avoid the temptation to style it.
And, we move along ...
3: Implement the Form Logic
Now, let's make sure our form will be useful.
To do that, we will:
- Initialize the component's form-related local state: This will involve adding pieces of state to match the form fields we have created: First Name, Email, etc.
- Add some event listeners and matching event handlers on our form's input fields. That way, we will bind any form field's entry to a corresponding piece of the component's local state. We will cover this in a moment.
So, let's do that.
Open the `Register.js` component file and update it like this:
import React, {useState} from 'react'
const Register = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
return (
<div>
<h3> Register for an Account</h3>
<p>
Fill out the form below to register for an account on the UserBase platform.
</p>
<form>
<label> First Name</label>
<input
type="text"
placeholder="Enter your first name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<label> Last Name </label>
<input
type="text"
placeholder='Your Last Name'
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
<label> Your Email </label>
<input
type="email"
placeholder="Enter a valid Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label> Your Password </label>
<input
type="password"
placeholder="Password with both numbers and letters"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<p>
<input
type="submit"
value="Register"
/>
</p>
</form>
</div>
)
}
export default Register
For now, ignoring any styling issues (which we still haven't touched on), our form can take input from the user and bind the entry into any of the form fields to a corresponding piece of the component's local state.
And, that's a LOT of progress.
But, we still have some more practical implementations to make our form more user-friendly and useful.
4: Event Handling, Form Processing and Making API Calls
For starters, we need to make sure that once the user hits that 'submit' button that we are able to submit the form data to our database. But since this is a frontend app, we won't have direct access to a database (because that would be a major web app security risk). Instead, we can make a `POST` call over HTTP to an API endpoint (that's already setup for this purpose) to save the user-submitted form data.
If you are a backend developer, you can create an API backend to handle these. If you work with a team that includes backend developers, they should be creating your backend APIs.
But for the sake of this developer guide, we will be using an existing API server.
The good folks at https://typicode.com have setup a "fake" API server with some endpoints that we can use for testing our API calls.
Since we have a form that lets a user fill in personal data to register for a new user account, we will just use the
https://jsonplaceholder.typicode.com/users endpoint to submit our form data from the `Register` component.
4.1: Setup API-Calling Configuratons
But, first, we need to code the API-calling logic from our React app.
Since we are being modular (i.e. having any set of related app logic in a folder of its own), let's `cd` into the `src/apis/` folder that we created earlier and put part of our API logic in there.
So, `cd src/apis` to step into the `apis` folder.
Next, let's create some parts of the configuration object, like the headers and the base URL endpoint.
So, create a new `apis/index.js` file with:
touch apis/index.js
Now, open that file and let's add some code to it.
Like this:
// Create the headers and our API backend's base/root API URL
const headers = {
'Content-Type': 'application/json',
}
const baseURL = "https://jsonplaceholder.typicode.com/users"
export { headers, baseURL}
We can reuse the `headers` and `baseURL` settings wherever and whenever we make API calls in our app.
And, speaking of making API calls ...
4.2: Make the API Call to Submit the Form Data
Let's add the logic for making the API call to submit the form data entered by the user.
There are a number of ways to do this.
But we will opt to implement the API call from inside the `Register` component itself.
Remember that we need to listen for when the user presses the "submit" button and then write some code to handle that "submit" event.
In practical terms, we will do a few things here:
- Add an `onSubmit` event listener to the root `<form>` element itself
- Create an event handler (a function, that is) to handle the "submit" event. We will simply call this function: `submitFormData()` and pass it the `event` object (which we will just refer to as `e` in our code here.)
- Implement the (frontend) form validation logic inside the event handler. We will also implement the logic for making the HTTP call to the API endpoint to submit the form data to the backend server.
- Pass the `submitFormData()` event handling function to the `onSubmit` event listener on the `<form>` element. (To prevent a catastrophe of executing the function when the component first mounts/loads, pass the event handler without the parentheses `()`.)
- import the `headers` and `baseURL` objects that we defined earlier into the `Register` component so we can use them to make the API call.
- Make the API call to submit the form data using global `fetch()` function. We will, of course, convert the form data to JSON before we make the API call with it.
So, open the `src/components/Register.js` file and update the component like this:
import React, {useState} from 'react'
import { headers, baseURL } from '../apis'
const Register = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [apiError, setApiError] = useState('')
const [submitError, setSubmitError] = useState('')
function submitFormData(e){
e.preventDefault();
// Check that the user has filled in all the form fields
if(firstName && lastName && email && password){
// Create an object from the form data
let userData = {
firstName, lastName, email, password
}
// Submit the form data to our API endpoint using the Fetch API
fetch(baseURL, {
method: 'POST',
body: JSON.stringify(userData),
headers: headers
})
.then(response => response.json())
.then(responseData => {
console.log(Object.entries(responseData))
})
.catch(error => setApiError(error))
}else{
setSubmitError('Please, correctly fill out all form fields.')
}
}
return (
<div>
<h3> Register for an Account</h3>
{apiError && (
<p style={{color:'red'}}> {apiError} </p>
)}
{submitError && (
<p style={{color: 'orange'}}>
{submitError}
</p>
)}
<p>
Fill out the form below to register for an account on the UserBase platform.
</p>
<form onSubmit={submitFormData}>
<label> First Name</label>
<input
type="text"
placeholder="Enter your first name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
/>
<label> Last Name </label>
<input
type="text"
placeholder='Your Last Name'
value={lastName}
onChange={(e) => setLastName(e.target.value)}
/>
<label> Your Email </label>
<input
type="email"
placeholder="Enter a valid Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<label> Your Password </label>
<input
type="password"
placeholder="Password with both numbers and letters"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<p>
<input
type="submit"
value="Register"
/>
</p>
</form>
</div>
)
}
export default Register
At this point, we have updated our `Register` component by:
- Adding an "onSubmit" event listener to the `<form>` element so that we can detect when a user hits the "Register" button on the form.
- Coding a "submitFormData()" event handler function (which will make sure that all form fields were filled in correctly and then submit the JSONified form data via an API call).
- Updating the JSX markup of the component to render any form-related and API-related errors.
So, the form processing logic is done. But our form still looks like something that was created by an uncreative grade schooler.
So, let's shine up the UI a bit.
5: Style the Form Component
There are a few ways you can approach styling your React components.
You can:
- write raw CSS styles right inside the JSX markup of your component. This is called inline styling and can be fast and useful if you are very good with CSS.
- create JavaScript style objects which you can then use in the JSX markup
- use external CSS stylesheets (whether created by you or from a CSS framework like Bootstrap)
- use a "styled components" library
For our use case and to style our app's components, we will:
- Install the `react-bootstrap` package and configure it for use across all components of the React app
- Refactor the JSX of the `Register` component to use the styled components of `react-bootstrap`
- Test to see that the component looks much better but works just as well as it did before
So, let's get coding ...
5.1: Install and Configure react-bootstrap
Head over to the terminal and install the react-bootstrap package (and its dependent package: the `bootstrap` CSS framework) with this command:
npm install bootstrap react-bootstrap
If you are using yarn, use this command:
yarn add bootstrap react-bootstrap
Once both packages are successfully-installed, let's add both the minified CSS and JS files of Bootstrap to the root `src/App.js` component file like this:
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.min.js'
From now on, as we build the components of this React app, we can use both the
Bootstrap style classes as well as the styled components of the
react-bootstrap package.
If you are using the CSS classes from Bootstrap, you won't need to import the "bootstrap" CSS or JS files into the component you want to use them in because you have already added the imports to the app root `App.js` component file. Adding the Bootstrap JS and CSS files to the root `App` component file makes them available for use by every component you create in that app.
If you are going to use the styled components from the react-bootstrap package, you will have to import the individual styled components that you want to use into the custom component that you are building.
In the following sections we will see how to do this.
5.2: Refactor the Component to use react-bootstrap
Next, let's go back to our `Register` component and use the styled components from react-bootstrap to style the form and other UI elements of that component.
So, open the `src/components/Register.js` file and update it like this:
import React, {useState} from 'react'
import { headers, baseURL } from '../apis'
import {Container, Form, Button, Alert, Row, Col} from 'react-bootstrap'
const Register = () => {
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [apiError, setApiError] = useState('')
const [submitError, setSubmitError] = useState('')
function submitFormData(e){
e.preventDefault();
// Check that the user has filled in all the form fields
if(firstName && lastName && email && password){
// Create an object from the form data
let userData = {
firstName, lastName, email, password
}
// Submit the form data to our API endpoint using the Fetch API
fetch(baseURL, {
method: 'POST',
body: JSON.stringify(userData),
headers: headers
})
.then(response => response.json())
.then(responseData => {
console.log(Object.entries(responseData))
})
.catch(error => setApiError(error))
}else{
setSubmitError('Please, correctly fill out all form fields.')
}
}
return (
<Container class="mt-5 mx-auto">
<h3 className='text-center'> Register for an Account</h3>
{apiError && (
<p className='text-danger'>
<strong> {apiError} </strong>
</p>
)}
{submitError && (
<Alert variant="danger">
<strong> {submitError} </strong>
</Alert>
)}
<p>
Fill out the form below to register for an account on the UserBase platform.
</p>
<Form onSubmit={submitFormData}>
<Form.Group>
<Form.Label> First Name</Form.Label>
<Form.Control
type="text"
placeholder="Enter your first name"
value={firstName}
onChange={(e) => setFirstName(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group>
<Form.Label> Last Name </Form.Label>
<Form.Control
type="text"
placeholder='Your Last Name'
value={lastName}
onChange={(e) => setLastName(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group>
<Form.Label> Your Email </Form.Label>
<Form.Control
type="email"
placeholder="Enter a valid Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
>
</Form.Control>
</Form.Group>
<Form.Group>
<Form.Label> Your Password </Form.Label>
<Form.Control
type="password"
placeholder="Password with both numbers and letters"
value={password}
onChange={(e) => setPassword(e.target.value)}
>
</Form.Control>
</Form.Group>
<Row>
<Col className='col-md-3 my-3 mx-auto'>
<Button type='submit' variant='primary'> Register </Button>
</Col>
</Row>
</Form>
</Container>
)
}
export default Register
You will notice that, to update our `Register` component, we:
- imported the styled components we want to use from the react-bootstrap package.
- replaced the whole component's markup with styled component including the outer `<div>` element which is now replaced by the `<Container>` component and so on.
- used the `<Form>` styled component and its child components (`<Form.Group>`, `<Form.Label>` and `<Form.Control>`) to apply the proper styles to the registration form.
At the end of it all, the component's UI looks much better without sacrificing any of the functionality that the component's logic needs.
So, what's next for this component?
6: Verify that all Works Correctly
Before we call it a day, let's load up the `Register` component in the browser to check that it all works as intended.
So, open your browser to "http://localhost:3000/register" and see the product of your React work.
If you encounter bugs and/or errors, you may have made some mistakes somewhere. Debug your code, use the Developer Tools and look at any relevant documentation.
Once it is running as intended, it is time to call it a day.
If you need to delve deeper into any of the topics touched on in thie guide, please, see the
Technical Reference section for a list of dev guides that go into more details about each topic. Or, sign up to be notified about when the
Fluent React course launches later this month.
Till I publish the next developer guide, you keep developing awesome React apps and making the world a better place for us all.
6: Technical Reference