Courses

React.js Client Parking App: Step-by-Step

Register implementation

In this lesson, we will implement form functionality to make a request to the server, and if the request was successful (the user was registered) redirect to the vehicles list page.

Register success

Register redirect

  1. First create a new src/views/vehicles/VehiclesList.jsx view for our future list. This is the page where the user will be redirected after registration.
function VehiclesList() {
return <div>There will be vehicles list</div>
}
 
export default VehiclesList

Add a new vehicles.index named route to the src/routes/index.jsx file.

const routeNames = {
'home': '/',
'register': '/register',
'vehicles.index': '/vehicles',
}

Define the new route in src/main.jsx right after the register route so the app can resolve the /vehicles path to the <VehiclesList> component.

import VehiclesList from '@/views/vehicles/VehiclesList'
<Route path={ route('register') } element={<Register />} />
<Route path={ route('vehicles.index') } element={<VehiclesList />} />

And add <NamedLink> to the vehicles.index route in the src/App.jsx file. The file now should look like this.

import { Outlet } from 'react-router-dom'
import NamedLink from '@/components/NamedLink'
 
function App() {
return (
<div className="App">
<header className="py-6 bg-gray-100 shadow">
<div className="container md:px-2 px-4 mx-auto">
<nav className="flex gap-4 justify-between">
<div className="flex gap-4 items-center">
<h2 className="text-xl font-bold">
<div
className="inline-flex items-center justify-center bg-blue-600 w-6 h-6 text-center text-white rounded mr-1"
>
P
</div>
myParking
</h2>
<NamedLink name="home">
Home
</NamedLink>
<NamedLink name="vehicles.index">
Vehicles
</NamedLink>
</div>
<div className="flex gap-4 items-center">
<NamedLink name="register">
Register
</NamedLink>
</div>
</nav>
</div>
</header>
<div className="container md:px-2 px-4 pt-8 md:pt-16 mx-auto">
<Outlet />
</div>
</div>
)
}
 
export default App
  1. Now to make requests from our registration form we need to install the Axios library. Run this command in your shell.
npm install axios --save

Then we need to import and configure Axios in the src/main.jsx file.

import axios from "axios";
 
window.axios = axios;
 
window.axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
window.axios.defaults.withCredentials = true;
window.axios.defaults.baseURL = "http://parkingapi.test/api/v1";

We set the X-Requested-With header to tell the server it is an XHR request, and it serves an additional purpose so the server must consent to CORS policies.

Option window.axios.defaults.withCredentials = true; tells the axios library to send the cookies along the request.

The convenience option is window.axios.defaults.baseURL = "http://parkingapi.test/api/v1"; so we can omit full URLs in our requests and just type in the relative path of the server's API endpoint.

Now we have a setup for making requests to API. The content of the src/main.jsx file looks like this.

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import axios from 'axios'
import App from '@/App'
import Home from '@/views/Home'
import Register from '@/views/auth/Register'
import VehiclesList from '@/views/vehicles/VehiclesList'
import '@/assets/main.css'
import { route } from '@/routes'
 
window.axios = axios
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'
window.axios.defaults.withCredentials = true
window.axios.defaults.baseURL = 'http://parkingapi.test/api/v1'
 
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path={ route('home') } element={<App />}>
<Route index element={<Home />} />
<Route path={ route('register') } element={<Register />} />
<Route path={ route('vehicles.index') } element={<VehiclesList />} />
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>,
)
  1. React comes with several built-in Hooks like useState. Sometimes you'll wish that there was a Hook for some more specific purpose: for example to fetch data, hold loading state, and errors. You might not find these Hooks in React, we can create Hooks for our application's needs.

Custom hooks let us share stateful logic, but not the state itself. Components using hooks don't have as much repetitive logic. When we extract logic into custom hooks, we can hide the gnarly details of how we deal with some external system or a browser API. The components then express intent and not the implementation.

Hook names must start with use followed by a capital letter. So let's create our first hook useAuth which will implement logic related to our application's auth processes.

Create a new src/hooks/useAuth.jsx file with the following content.

import { useNavigate } from 'react-router-dom'
import { route } from '@/routes'
 
export function useAuth() {
const navigate = useNavigate()
 
async function register(data) {
return axios.post('auth/register', data)
.then(() => {
navigate(route('vehicles.index'))
})
.catch(() => {})
}
 
return { register }
}

Here we define the register function to make a call to http://parkingapi.test/api/v1/auth/register. Remember we don't need to define the full URL because we have baseURL set for Axios. If the request is successful then we have another hook useNavigate from React Router to navigate us to the vehicles.index route.

The useAuth() hook returns the register function, so we can use that function in our <Register> component.

  1. Update the src/views/auth/Register.jsx component with the following content.
import { useState } from 'react'
import { useAuth } from '@/hooks/useAuth'
 
function Register() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [passwordConfirmation, setPasswordConfirmation] = useState('')
const { register } = useAuth()
 
async function handleSubmit(event) {
event.preventDefault()
 
await register({ name, email, password, password_confirmation: passwordConfirmation })
 
setPassword('')
setPasswordConfirmation('')
}
 
return (
<form onSubmit={ handleSubmit } noValidate>
<div className="flex flex-col mx-auto md:w-96 w-full">
 
<h1 className="heading">Register</h1>
 
<div className="flex flex-col gap-2 mb-4">
<label htmlFor="name" className="required">Name</label>
<input
id="name"
name="name"
type="text"
value={ name }
onChange={ event => setName(event.target.value) }
className="form-input"
autoComplete="name"
/>
</div>
 
<div className="flex flex-col gap-2 mb-4">
<label htmlFor="email" className="required">Email</label>
<input
id="email"
name="email"
type="email"
value={ email }
onChange={ event => setEmail(event.target.value) }
className="form-input"
autoComplete="email"
/>
</div>
 
<div className="flex flex-col gap-2 mb-4">
<label htmlFor="password" className="required">Password</label>
<input
id="password"
name="password"
type="password"
value={ password }
onChange={ event => setPassword(event.target.value) }
className="form-input"
autoComplete="new-password"
/>
</div>
 
<div className="flex flex-col gap-2">
<label htmlFor="password_confirmation" className="required">Confirm Password</label>
<input
id="password_confirmation"
name="password_confirmation"
type="password"
value={ passwordConfirmation }
onChange={ event => setPasswordConfirmation(event.target.value) }
className="form-input"
autoComplete="new-password"
/>
</div>
 
<div className="border-t h-[1px] my-6"></div>
 
<div className="flex flex-col gap-2 mb-4">
<button type="submit" className="btn btn-primary">
Register
</button>
</div>
</div>
</form>
)
}
 
export default Register

Here we import the useAuth hook and register function.

import { useAuth } from '@/hooks/useAuth'
 
// ...
 
const { register } = useAuth()

Then updated the handleSubmit() function to call the register function from the useAuth hook.

async function handleSubmit(event) {
event.preventDefault()
 
await register({ name, email, password, password_confirmation: passwordConfirmation })
 
setPassword('')
setPasswordConfirmation('')
}

We also added

setPassword('')
setPasswordConfirmation('')

Usually when you submit the form browser would automatically clear fields with type="password", but because we are preventing default behavior this won't happen by default and we need to do that manually to retain the same functionality. This code will be always executed disregarding if the request resulted in success or error which is the default behavior when posting forms.

After submitting the form the following response from the server will be returned:

{ access_token: "118|CJv0t9yHJcPFsifkXyoMaiCJBpRyrmsXYDLENyAN" }

And you will be redirected to the /vehicles URL, but we still have a few things missing.

  • Register form has no validation if some data is incorrect and missing
  • When we register we don't save the token anywhere for protected API endpoints.

Let's finish with the form first and implement validation in the next lesson.

Previous: Register page
avatar

Hello, I have this error when try to send the form:

xhr.js:247 POST http://localhost:5174/api/V1/auth/register 404 (Not Found)

avatar
  1. In your .env file, do you have APP_URL=http://localhost:5174?
  2. In your RouteServiceProvider, did you specify V1 prefix?
  3. Don't remember exactly but shouldn't V1 be lowercase v1?
avatar

This should come from Backend (Laravel) I think. For localhost:5174 it is from frontend React(Vite).

You must find car-parking app api laravel then You should built it. Maybe url should be

http://127.0.0.1:8000/api/V1/auth/register instead.

For http://parkingapi.test/api/v1/auth/register it is just a dummy one.

avatar
You can use Markdown
avatar

In case you're stuck with the API, you have to git clone this project: https://github.com/LaravelDaily/Laravel-API-Parking-Demo.git. Then, install it as a regular Laravel app (composer install, npm install, artisan migrate, artisan key:generate), run it with Valet (careful of the app directory's name to match API url) / Docker and you're good to go!

avatar
You can use Markdown
avatar
You can use Markdown