Errors are awesome. Errors tell you that something needs to be fixed. If one would not have the error, it would be very frustrating as things would not work and we would not know why. The more specific an error is, the more helpful it is. The best errors are the ones that tell you what is wrong and what you can do to fix it.
When users sign up for an account, or fill out a form on an app, there are (hopefully) validations being checked before a new instance is added to the database. If the data being submitted is not valid, the best case scenario would be for a very specific error message to show up on the screen and direct the user in what to do to submit their information correctly. In this post we will describe how to grab an error from a rails backend and conditionally display it on a react frontend.
Let's get started!
1. Validations in the Model
For each model that you create, you can specify what validations are necessary. To do this you would use Active Record validation methods.
In the example below, a user signs up for an account and creates a new user instance. These validations check that
there is a username
the username is unique
there is a password
there is an email address
the email address fits the criteria of a valid email address
class User < ApplicationRecord
validates :name, uniqueness: true
validates :password, presence: true
validates :email, presence: true, format: /\w+@\w+\.{1}[a-zA-Z]{2,}/
end
If any of these requirements are not met, the user will not be able to sign up. At the same time an error is being generated based on these validations. Now, how will the user see the error and know where they went wrong?
2. Controller Error Handling
The next step takes place in the controller. In the User Controller, there is a create method that handles adding a new user.
def create
user = User.create!(user_params)
session[:user_id] = user.id
render json: user, status: :created
rescue ActiveRecord::RecordInvalid => e
render json: { errors: e.record.errors.full_messages }, status: :unprocessable_entity
end
In this case, it is necessary to use the .create! method, as the bang operator (!) will throw an error if saving fails due to the data not passing the validations. If that is the case, we will render a json object that displays an array of error messages.
{
"errors": [
"Name has already been taken",
"Email is invalid"
]
}
3. Save the Error to React State on the Frontend
Now is when we can finally send the error to the frontend. The best way to capture the value of the error is to save it to state. State should initially be set as false, as no errors actually exist on the page load.
const [errors, setErrors] = useState(false)
Then comes the submit form function. Within the submit is a post request to the backend, sending the new user object to be validated.
function handleSubmit(e) {
e.preventDefault()
const user = {
name,
password,
passwordConfirmation,
email
}
fetch('/signup', {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(user)
})
.then((r) => {
if(!r.ok) {
return r.json().then((e) => setErrors(Object.values(e).toString()))
} else {
return r.json().then((data) => dispatch(signupUser(data)))
}
})
}
We need two different ways to handle the response, based on wether or not the post request was successful. If the information was successfully saved, then the user information is sent to my redux store to be processed. If not, we need to set the errors in state to the error values of the json object. I converted the array into a string so it would not have to be mapped before displaying.
4. Display the Error
Finally! The error is saved in a string format in state. Now all that is needed to display the error to the user is a simple ternary expression. If there is an error, display the error. If not, don't.
{ errors ? <h5 class="text-danger">{errors}</h5> : null }
Top comments (0)