Handling the Most basic Login with React and Sinatra
Tags: javascript, beginners, ruby, react
Published: 2021-09-04 18:09:23

## Introduction
In this article I will be explaining how to handle a basic login that only uses a `username` for authentication.  Part of the purpose is to be able to mock having a currentUser on the front end without getting into real authentication. For this project we will be using React on the front-end and Sinatra on the server side.
## Handling a Successful Login
### Setting Up The Basic Route Server-Side
First we're going to set up the route for our server to perform a GET request.  Since we're going to be authenticating the user by the username it's quite simple:
*In my application the user `has_many` trips, which we will want access to later.*
```ruby
get "/users/:username" do
    user = User.find_by_username(params[:username])
    user.to_json(include: [:trips])
  end
```

### Setting up the Login Client-Side
We are going to keep it simple here as well, just a basic input with a submit button. First we're going to set up our basic functions for handling the submit and change for the form data. Don't worry about `findCurrentUser`, we'll get to that in a bit.
```javascript
const [userLogin, setUserLogin] = useState("")
const handleSubmit = (e) => {
  e.preventDefault()
  findCurrentUser(userLogin)
}
const handleChange = e => {
  setUserLogin(e.target.value)
}
```
Using our state we can set up our form for a very basic login:

```html
<div>
  <form onSubmit={handleSubmit}>
     <label htmlFor="login" value="Username">Username:   </label><br/>
     <input type="text" name="login" value={userLogin} onChange={handleChange} autoFocus={true}/>
     <input type="submit" value="Login"/>
   </form>
</div>
```
![an input field with a username label and a submit button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8jf41uvbkv1616ipbkd.PNG)

### Making the GET Request
Now to set up the fetch, we're going to be using async and await to help our code look a little cleaner and easier to understand:
*I'm using the useHistory hook from [react-router-dom](https://reactrouter.com/web/api/Hooks/usehistory) to redirect the user to their trips page after a successful login*
```javascript
async function findCurrentUser(username) {
      const response = await fetch(`${baseUrl}/users/${username}`)
      const user = await response.json()
      changeUser(user)
      history.push(`/users/${user.id}/trips`)
      }
    }
```
We are also bringing `changeUser` from our App component via props to handle the state of currentUser:
```javascript
function App() {
  const [currentUser, setCurrentUser] = useState(null)
  
  const changeUser = (user) => {
    setCurrentUser(user)
  }
return (
 <Route exact path="/login">
     <Login changeUser={changeUser}/>
 </Route>
)
}
```
With all of that set up someone should be able to successfully login!
![the login page from earlier with traveladdict in the username field](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zastxavhhiyg50ywxb9u.PNG)
Success!!!
![a new page with a list of trips for the user that just logged in. The nav bar also changed a few of it's options](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5ml8buvmmrb2txne7x5d.PNG)

## Failed Login Attempt
### Handling Failed Attempt Server-Side
But what happens when someone tries to login, and they don't already have an account?
![an error message that says:Unhandled Rejection (TypeError): Cannot read property 'id' of null](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mipuw19f5m6kx42anskv.PNG)

First let's look at how to handle this error on the server-side. Sinatra has a helper method `status` that we are going to take advantage of. It allows us to change the HTTP response status code. 401 is the status code for unauthorized/unauthenticated, which seems to fit what we want to return.  So if the user exist, return the user, else change the status code to 401 and return an error message.

```ruby
get "/users/:username" do
    user = User.find_by_username(params[:username])
      if user
        user.to_json(include: [:trips])
      else
        status 401
        { errors: "user doesn't exist" }.to_json
      end
  end
```
### Handling Failed Attempt Client-Side
Now that we are changing the status code on a failed attempt, we can use that to deal with the client side of things.  We can use response.status to access the status code, and if it equals 401, do nothing, otherwise perform the actions of a successful login.

```javascript
async function findCurrentUser(username) {
      const response = await fetch(`${baseUrl}/users/${username}`)
      if (response.status === 401) {
        return null
      } else {
        const user = await response.json()
        changeUser(user)
        history.push(`/users/${user.id}/trips`)
      }
    }
```
Let's give it a try now:
![the login screen that was displayed earlier with the console open to the right. In the console we see a failed GET request with a 401 status code](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e9n7yn82t5lgnmd352e5.PNG)

Great! Now we're no longer getting an error when someone has a failed login attempt, but there's one problem, when our user tries to login, it looks like nothing is happening for them. Let's fix that by giving our user an error message.

###Displaying an Error message
To handle the display of an error message, we are going to handle it with useState:
`const [error, setError] = useState(null)`
And we're going to change our fetch just a little bit, instead of just returning null, we're going to set an error message:
```javascript
    async function findCurrentUser(username) {
      const response = await fetch(`${baseUrl}/users/${username}`)
      if (response.status === 401) {
        setError("That user doesn't exist, try again or sign up for an account!")
      } else {
        const user = await response.json()
        changeUser(user)
        history.push(`/users/${user.id}/trips`)
      }
    }
```

Now that we have our error message all we need to do is display it, and since we are using state and setting it to null on render it will only display when it has been changed from null. 

```html
<div>
      <form onSubmit={handleSubmit}>
        <h3 style={{color:"red"}}>{error}</h3>
        <label htmlFor="login" value="Username">Username:</label><br/>
        <input type="text" name="login" value={userLogin} onChange={handleChange} autoFocus={true}/>
        <input type="submit" value="Login"/>
      </form>
    </div>
```
So that it stands out I've put it as an h3 with text color of red.

## Conclusion
That's the basics of how to handle both a successful and failed login attempts with just a username, now you can try to figure out how to handle what's going to get displayed based on the current user being logged in or not. Good luck!

#### Extras
For more information on some of the things I mentioned checkout:
[useHistory Hook](https://reactrouter.com/web/api/Hooks/usehistory)