Courses

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

Validation and register form

Our objective in this lesson is to display form errors on the client side.

Error validation

  1. When submitting forms it is crucial to indicate if the server doesn't accept the request and if some information provided needs to be clarified. In such cases, Laravel returns a response with more details. In our case, if we submit an empty form Laravel returns a response with a 422 status code with an object which looks like this:
{
"message": "The name field is required. (and 2 more errors)",
"errors": {
"name": [
"The name field is required."
],
"email": [
"The email field is required."
],
"password": [
"The password field is required."
]
}
}

Considering that we are going to have more forms in the future it is a good idea to have a component where we can pass errors object and a field name to display particular error messages under the specific field.

Now let's extend our useAuth hook src/hooks/useAuth.jsx by adding a new errors state, this is where we are going to store the errors object returned from the backend.

The src/hooks/useAuth.jsx file should look like this.

import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { route } from '@/routes'
 
export function useAuth() {
const [errors, setErrors] = useState({})
const navigate = useNavigate()
 
async function register(data) {
setErrors({})
 
return axios.post('auth/register', data)
.then(() => {
navigate(route('vehicles.index'))
})
.catch(error => {
if (error.response.status === 422) {
setErrors(error.response.data.errors)
}
})
}
 
return { register, errors }
}

When the server returns error code 422 Unprocessable entity we set the errors state with the values the server returned in the catch clause.

setErrors(error.response.data.errors)

On every new submission, we want to reset the errors state, this is done with the setErrors({}) line.

  1. The hook has been set up and now it is a lot easier to implement the component to display error messages in form.

Create a new src/components/ValidationError.jsx component.

import PropTypes from 'prop-types'
 
function ValidationError({ errors, field }) {
return errors?.[field]?.length &&
<div className="alert alert-danger" role="alert">
<ul>
{ errors[field].map((error, index) => {
return (<li key={ index }>{ error }</li>)
}) }
</ul>
</div>
}
 
ValidationError.propTypes = {
errors: PropTypes.object.isRequired,
field: PropTypes.string.isRequired,
}
 
export default ValidationError

Like we did with the NamedLink component, we define properties errors and field that component will accept and display errors for a field where the object key matches.

  1. Now we can update our src/views/auth/Register.jsx component. To display error messages is pretty straightforward now.
import { useState } from 'react'
import { useAuth } from '@/hooks/useAuth'
import ValidationError from '@/components/ValidationError'
 
function Register() {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [passwordConfirmation, setPasswordConfirmation] = useState('')
const { register, errors } = 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"
/>
<ValidationError errors={ errors } field="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"
/>
<ValidationError errors={ errors } field="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"
/>
<ValidationError errors={ errors } field="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 imported the ValidationError component.

import ValidationError from '@/components/ValidationError'

Unpacked errors state from useAuth hook.

const { register, errors } = useAuth()

And added the ValidationError component after each input value by passing the errors state and field name which matches keys from the API response.

<ValidationError errors={ errors } field="name" />
<ValidationError errors={ errors } field="email" />
<ValidationError errors={ errors } field="password" />

password_confirmation field never returns error messages, so we do not need to add it there. Password-related errors are always returned under the password key.

Previous: Register implementation
avatar

can you help me to solve this problem : CSRF token mismatch how i can solve this isuue frontend : https://github.com/DvOmar76/auth-frontend backend: https://github.com/DvOmar76/backend-auth/

avatar

To solve this issue you have 2 options:

  1. Disable CSRF token for API endpoints.
  2. First make a request to the API to retrieve the CSRF token.
avatar
You can use Markdown
avatar
You can use Markdown