3

I'm building my first React JS system and utilising Laravel Passport for my authentication stuff. I have it working really well up until I try and submit invalid data. When I send my post request, I have the following Request class set up:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class RegisterUser 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|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|confirmed|min:8'
        ];
    }
}

Which is triggered from the /api/register end point seen in the following React JS method:

registrationHandler = e => {
        e.preventDefault();

        $("#email-login-btn")
            .attr("disabled", "disabled")
            .html(
                '<i class="fas fa-spinner fa-spin fa-1x fa-fw"></i><span class="sr-only">Loading...</span>'
            );

        var formData = new FormData();
        formData.append("email", e.target.elements.email.value);
        formData.append("name", e.target.elements.name.value);
        formData.append("password", e.target.elements.password.value);
        formData.append(
            "password_confirmation",
            e.target.elements.password_confirmation.value
        );

        axios
            .post("/api/register", formData)
            .then(response => {
                return response;
            })
            .then(json => {
                if (json.status == 201) {
                    const MySwal = withReactContent(
                        Swal.mixin({
                            toast: true,
                            position: "top-end",
                            showConfirmButton: false,
                            timer: 3000
                        })
                    );

                    MySwal.fire({
                        type: "success",
                        title: "Registered successfully"
                    });
                } else {
                    $("#email-login-btn")
                        .removeAttr("disabled")
                        .html("Register");
                }
            })
            .catch(error => {
                console.log(error.response.data.errors);
                $("#email-login-btn")
                    .removeAttr("disabled")
                    .html("Register");
            });
    };

You can see that at the bottom I'm trying to catch the errors and log them to the console with console.log(error.response.data.errors); - this works great, I can see them in the console like this:

enter image description here

My controller method is this, nothing crazy:

    {
        // If validation hasn't failed, register the user
        User::create([
            'name' => request('name'),
            'email' => request('email'),
            'password' => bcrypt(request('password'))
        ]);

        return response()->json(['status' => 201]);
    }

But what I'm trying to do now is show the errors on the register page, under my form. I've tried setting the errors inside the state (where I'm doing the console log) and then pass them as props to my Register.js file, but it always returns "undefined", presumably because the routes aren't changing.

Is there a better way to display the errors that the system sends back? I'm at a bit of a loss with this currently.

Thanks!

2 Answers 2

2

So I achieved this by doing a couple of things:

first of all, I created a new child component to handle the display of the errors, Error.js

import React from "react";

class Error extends React.Component {
    render() {
        return (
            <>
                <li className="text-danger">{this.props.message}</li>
            </>
        );
    }
}

export default Error;

I then imported this at the top of my Register.js file.

import Error from "./Error";

And then in the render() function inside Register.js I added this block:

<ul>
    {Object.keys(errors).map((error, index) => (
        <Error
            message={errors[error][0]}
            key={shortid.generate()}
        />
    ))}
</ul>

You'll notice that I'm using shortid.generate() as my key, this is because the data I received from Laravel didn't actually contain anything I could utilise as a unique key for each item. This package seems to work really well in my case.

The reason my original idea of looping over the array of errors using .map was failing is because Laravel was actually sending me back an object, rather than an array. So switching this idea for something that looped over the object worked perfectly - Object.keys(errors).map((error, index)

Sign up to request clarification or add additional context in comments.

Comments

0

I know the question got answered but I am giving this answer to help those who want to work with "granular distribution of errors", by granular means like when the Laravel gives you an object of "errors" as a response so you can take every single error and display it near the input field when the error is present for better presentation, I would share the image. I am using react-bootstrap but the concept is similar in every case. note: the whole answer is concept-oriented, kindly focus on the concept. I explained everything in depth.


enter image description here

you can simply take each error from the "errors object ".

    import React, { useState } from "react";
      import axios from "axios";


    function Registration(){
      const [userRegistrationData, setUserRegistrationData] = useState({ username:''});

      const [userRegErrors, setUserRegErrors] = useState({usernameError:''});

    const onInputchange = (e) => {

    setUserRegistrationData({...userRegistrationData, [e.target.name]: e.target.value });
      };

    
    const registerUser=(e)=>{
      e.preventDefault();

   

     let username = userRegistrationData.username
        
        //you can use Axios here, that Axios function will return the validation response or errors response from your Laravel API
        
      //note the errors would be caught in the catch block of your code, you can check your data by console log and then do your work accordingly. you can take an example from below

          axios.get('/user?ID=12345')
       .then(function (response) {
    // handle success
    console.log(response);
  })
       .catch(function (error) {
    // handle error
    console.log(error.response);
      setUserRegErrors({usernameError:error.response.errors.username});
  })
      .then(function () {
    // always executed
  });
     }
          
    }
    
    
    let usernameError;
    
    //userRegistrationErrors.error is the variable which has store the object of errors it is for concept purposes, you can use axios to fetch the data
    if(userRegErrors.error){
    usernameError=userRegErrors.error.username;
       }
    
     
      return (  <> 
        <form onSubmit={registerUser} >
        <FloatingLabel  label="Username">
                          <Form.Control
                            type="input"
                            placeholder=" "
                            name="username"
                            value={userRegistrationData.username}
        
                            required
                            onChange={(e)=>onInputchange(e)}
                            isInvalid={usernameError?true:false}
        
                          />
                        </FloatingLabel>
                        {usernameError?<div className="validation-error">The username already exists in our system !</div>:''}
        
        </form>
    </>
    )
        }
    
    export default Registration

2 Comments

Can you clean your answer's formatting up a bit please and make sure that your code is contextual :)
Sure @AndyHolmes

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.