Handling errors with React and Laravel
Today, I will share with you the way that I handle erros with React + Laravel .
Handling errors from a Laravel API in a React application involves making API requests, capturing potential errors, and displaying meaningful messages to the user. Below is a step-by-step tutorial to guide you through this process:
Step 1: Set up Laravel API
Make sure your Laravel API is properly set up and configured to return meaningful error responses.
Set the API router -
Route::post('/register', [AuthUserController::class, 'register']);
Set the Controller -
public function register(RegisterUserRequest $request) {
return response()->json(null, 200);
}
And Finally set The Request
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'name' => 'required|string|min:3|max:32',
'email' => 'required|email|unique:users,email',
'password' => 'required|string|min:6|max:8',
'password_confirmation' => 'required|string|same:password',
];
}
}
Step 2 — Start a React Project
npx create-react-app {name_of_app}
Install dependecies
npm install @reduxjs/toolkit
npm install react-redux
npm i react-router-dom@6
npm install axios
npm install sweetalert2
Create the Script
import './App.css';
import React, {useState} from 'react'
import {useDispatch} from "react-redux"
import Errors from "./hooks/ErrorInput"
import Swal from "sweetalert2";
import { store } from "./store/modules/registerUserSlice.js"
import SubmitBtn from "./components/SubmitBtn"
function App() {
// const [ErrorsData, setErrorList] = useState({})
const ErrorsData = new Errors({})
const [errorList, setErrorList] = useState({})
const dispatch = useDispatch()
const [data, setData] = useState({
procesing: false,
name: '',
email: '',
password: '',
password_confirmation: ''
})
const nameChangeHandler = (event) => {
setData ((prevState) =>
{ return { ...prevState, name: event.target.value}
})
}
const emailChangeHandler = (event) => {
setData ((prevState) =>
{ return { ...prevState, name: event.target.email}
})
}
const passwordChangeHandler = (event) => {
setData ((prevState) =>
{ return { ...prevState, name: event.target.password}
})
}
const passwordConfirmationChangeHandler = (event) => {
setData ((prevState) =>
{ return { ...prevState, name: event.target.password_confirmation}
})
}
const submitHandler = event => {
event.preventDefault();
setData ((prevState) =>
{ return { ...prevState, procesing: true }
})
// filters
const wait = setTimeout(() => {
clearTimeout(wait)
// DISPATCH
dispatch(store(data)).then((res) => {
// SUCCESS MESSAGE
Swal.fire({
title: 'Success !!',
text: `The User Was Registered`,
icon: 'success',
showConfirmButton: false,
timer: 1500
})
}).catch(error => {
// RESET ERROR LIST
// SET EERROR REQUEST
// ErrorsData.record(error.data.errors, 'form')
ErrorsData.record(error.data.errors, 'form')
Object.keys(error.data.errors).forEach(function(key) {
if(ErrorsData.has('form.' + key)){
setErrorList ((prevState) =>
{ return { ...prevState, [key]: ErrorsData.get('form.' + key)}
})
}
});
}).then(() => {
// LOADING FALSE
setData ((prevState) =>
{ return { ...prevState, procesing: false }
})
})
})
}
return (
<div className="App">
<div className="container">
<form method="POST" onSubmit={submitHandler}>
<fieldset>
<div className="field is-horizontal">
<div className="field-label">
<label className="label">Name</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<input className="form-control ipone" name="name" type="text" onChange={nameChangeHandler} />
{errorList.name && <p className="error-msg">{errorList.name}</p>}
</div>
</div>
</div>
</div>
<div className="field is-horizontal">
<div className="field-label">
<label className="label">E-mail</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<input className="form-control ipone" name="email" type="text" onChange={emailChangeHandler} />
{errorList.email && <p className="error-msg">{errorList.email}</p>}
</div>
</div>
</div>
</div>
<div className="field is-horizontal">
<div className="field-label">
<label className="label">Password</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<input className="form-control ipone" name="password" type="text" onChange={passwordChangeHandler} />
{errorList.password && <p className="error-msg">{errorList.password}</p>}
</div>
</div>
</div>
</div>
<div className="field is-horizontal">
<div className="field-label">
<label className="label">Password Confirmation</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<input className="form-control ipone" name="password_confirmation" type="text" onChange={passwordConfirmationChangeHandler} />
{errorList.password_confirmation && <p className="error-msg">{errorList.password_confirmation}</p>}
</div>
</div>
</div>
</div>
<div className="field is-horizontal">
<div className="field-label is-small">
</div>
<div className="field-body">
<div className="field">
<div className="control">
<SubmitBtn
processloading={data.procesing}
stylebutton="btn_cl_left btn-green-md1"
textbutton="Salvar"
/>
</div>
</div>
</div>
</div>
</fieldset>
</form>
</div>
</div>
);
}
Create The Store -
this will conect to the API that is in this url — http://localhost:8080/api/register
if does have erros , will return it to the script .
import { createSlice } from "@reduxjs/toolkit";
import axios from "axios";
const initialState = [
{}
]
const pageInfo = {
STORE_URL: 'http://localhost:8080/api/register'
}
const registerUserSlice = createSlice({
name: 'registerUser',
initialState,
reducers: {
}
})
export const store = (data) => {
console.log('data', data)
return async () => {
const fetchData = async () => {
const response = await axios.post(pageInfo.STORE_URL, data)
return await response.data;
};
try {
return await fetchData();
} catch (error) {
throw error.response
}
}
}
export default registerUserSlice.reducer
And finaly our Error Class . that will handle the errors that comes from Laravel -
class Errors {
constructor () {
this.errors = {}
}
// GET ERRRO
get (field) {
// IF IS AN ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')
// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.0.IMAGES
if (splitString.length === 4) {
// LARAVEL FORM ERROR
const errorOne = this.errors[splitString[0]][splitString[1]][splitString[2] + '.' + splitString[3]]
if (errorOne) {
return errorOne
}
if (this.errors[splitString[0]][splitString[1]][splitString[2]][splitString[3]]) {
return this.errors[splitString[0]][splitString[1]][splitString[2]][splitString[3]][0]
}
}
// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.IMAGES
if (splitString.length === 3) {
// LARAVEL FORM ERROR
const errorOne = this.errors[splitString[0]][splitString[1] + '.' + splitString[2]]
if (errorOne) {
return errorOne
}
if (this.errors[splitString[0]][splitString[1]][splitString[2]]) {
return this.errors[splitString[0]][splitString[1]][splitString[2]][0]
}
}
// ADD ERROR INSIDE ARRAY LIST -> FORM.TITLE
if (this.errors[splitString[0]][splitString[1]]) {
return this.errors[splitString[0]][splitString[1]][0]
}
return false
}
// VALIDATE WITH NO FORM REFRENCE -> TITLE
if (this.errors[field]) {
return this.errors[field][0]
}
}
record (errors, list = null) {
this.clear()
console.log('vali salvarrrrrrrrrr')
if (list) {
this.errors = [list]
this.errors[list] = errors
return
}
this.errors = errors
}
// RESET RECORDS
reset () {
this.errors = {}
}
// ANY
any () {
return Object.keys(this.errors).length > 0
}
// HAS
has (field) {
// IF IS AN ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')
if ({}.hasOwnProperty.call(this.errors, splitString[0])) {
// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.0.IMAGES
if (splitString.length === 4) {
if ({}.hasOwnProperty.call(this.errors[splitString[0]][splitString[1]], splitString[2])) {
// LARAVEL FORM ERROR
const errorOne = this.errors[splitString[0]][splitString[1]][splitString[2] + '.' + splitString[3]]
if (errorOne) {
return errorOne
}
// MY FORM ERROR
return {}.hasOwnProperty.call(this.errors[splitString[0]][splitString[1]][splitString[2]], splitString[3])
}
return false
}
// IT IS AN ARRAY FORM -> FORM.IMAGE_GALLERY.IMAGES
if (splitString.length === 3) {
// LARAVEL FORM ERROR
const errorOne = this.errors[splitString[0]][splitString[1] + '.' + splitString[2]]
console.log('errorOne', errorOne)
if (errorOne) {
return errorOne
}
// MY FORM ERROR
// IT HAS FORM REFERENCE -> FORM.TITLE
const data = {}.hasOwnProperty.call(this.errors[splitString[0]], splitString[1])
if (!data) {
return false
}
return {}.hasOwnProperty.call(this.errors[splitString[0]][splitString[1]], splitString[2])
}
// IT HAS FORM REFERENCE -> FORM.TITLE
return {}.hasOwnProperty.call(this.errors[splitString[0]], splitString[1])
}
return false
}
// DOES NOT HAVA FORM REFERENCE -> TITLE
return {}.hasOwnProperty.call(this.errors, field)
}
// CLEAR
clear (field) {
if (field) delete this.errors[field]
this.errors = {}
}
// VERIFY ERROR AND CLEAR
verifyErrorAndClear (field) {
if (this.has(field)) {
this.clearField(field)
}
}
// CLEAR FIELD
clearField (field) {
// HAS ARRAY ERROR
if (field.includes('.')) {
const splitString = field.split('.')
const listName = splitString[0]
const fieldName = splitString[1]
delete this.errors[listName][fieldName]
return
}
delete this.errors[field]
}
}
export default Errors
Final Result
Conclusion
This tutorial provides a basic structure for error handling in a Vue.js application that communicates with a Laravel API. Customize it based on your specific API endpoints and error response structure.
The Git Hub Project -
Check as well how to use the same class, using vue js -
https://medium.com/@murilolivorato/handling-errors-with-vue-and-laravel-ea652603a495
Thanks a lot for reading till end. Follow or contact me via:
Github:https://github.com/murilolivorato
LinkedIn: https://www.linkedin.com/in/murilo-livorato-80985a4a/
Youtube : https://www.youtube.com/@murilolivorato1489