Let me in! Login/Register forms with React+Rails

Emil Andreasyan
7 min readAug 22, 2020

The most common pattern that you may encounter when entering a website, is likely to be login/register part when you have to fill your unique credentials in order to get access to the data specifically designated for you to view and/or manage. It can seem simple, yet be very challenging to build, especially considering using both front-end and back-end, when the data you insert in React form travels to Rails back-end and gets approved or denied with further prompts. In this article, we’ll try to build a fully functioning login/register page using React and Rails. So, buckle up and get ready for some fun.

After creating Rails and React applications by running rails new app_name and npm install -g npm (to ensure that we have the newest version of npm)
npx create-react-app app_name, respectively, make sure that you run your applications on different servers, while by default they live in “http://localhost:3000”. To solve this issue, we can either in package.json > “scripts” (React) specify the port with “start”: “PORT=3001 react-scripts start”, or in Rails terminal run rails s -p 3001. If we choose the latter options, it will mean, that from this moment on React will, by default, run on port 3000, while Rails will do it on brand new port 3001, thus making sure they will not collide.

Rails

In our Gemfile, at least two gems should be installed, one refers to having a strong, encrypted password which is compelling to guess and practically impossible to break (even the Software Engineer doesn’t see the password, only the salted version of it!), and another gem which allows Cross-Origin Resource Sharing (CORS)

gem ‘bcrypt’, ‘~> 3.1.7’

gem ‘rack-cors’, ‘~> 1.1’, ‘>= 1.1.1’

Few things to note about secure password are that in tables it should be indicated as password_digest, and in User model, it is important to insert has_secure_password expression so that all this encryption process works seamlessly.

Needless to say, after adding any gem into our Gemfile, it’s required to run bundle or bundle install so that we reap all the benefits of this little treasures.

After that, to avoid another conflict, in Rails terminal, by running touch config/initializers/cors.rb, we should create a ruby file, and add several lines:

This will ensure avoiding CORS conflict when an API is called from front-end and also make possible asynchronous AJAX requests.

Another important ruby file to build, if we want to have benefits of the session and cookies, is session store, we can complete that by running touch config/initializers/session_store.rb, and, after that, adding the following lines there, where, in case of production, we can check whether we should store our cookies in the real-world web site domain like “app-name.herokuapp.com” (actually, not a real-world web site, but could be), or by default in localhost.

SessionController is the place when we store the current user, the user that has an account, and successfully logged in, in other words, we authorize the existing user. In the code below we first try to find the user by email (unique parameter). If so, we’ll try further to authenticate the user by the password with the built-in try method (so many try-s!). If the user is found, we will store its data into session data, otherwise, the Rails will return 401 (unauthorized) error.

Things are a bit different compared to user registration. We should underline, that, unlike sessions, where we search for the user by email and authenticated by password, we simply create a new user by the params (fields filled in the forms). If the user was created (by unique email and password complexity), we will store that user in session (like in the example above), otherwise, we will prompt with 500 (Internal Server) error.

So, the SessionController should be responsible for authorizing existing user, while RegistrationController should be responsible for creating a new user

It is also important to make sure whether the user is currently logged in or not, thus granting access to some sensitive data designated for the specific person, while we don’t want that one user to mistakenly delete an image or withdraw an amount of money from the bank account belonging to someone else. We can create helper methods verifying the status and using these methods in our controllers.

Ok, after some really hard work done (some steps were omitted for the sake of brevity though), we can finally jump into the React part and seek to connect and communicate our back-end and front-end parts, making the dialogue complete.

React

In React side, first of all, we should specify our main controller, which has state for changing data, and a method called handleLogin. We nest our Home and Dashboard children components and wrap them in BrowserRouter and Switch we imported from ‘react-router-dom’. By doing so, we can now pass in-built Route props, like match, location, history (via {…props}) along with ordinary props of our own creation like handleLogin function.

The next thing in line should be the creation of Home component, which has an authorization method (handleAuth) that will be passed to the children components of Registration and Login (are not created yet). As the comments suggest, if the user is successfully logged in or registered, he/she will be redirected “/dashboard” address, which will render the Dashboard component, and this is where we want to eventually get. And if you wonder what on earth this.props.history means (the keyword is “history”), you should take another look at the previous picture, when we passed our Home component in Route.

So far it was the easiest part of React. From now on, we can go much deeper and connect our Registration controlled component to the back-end. For that reason, we will need to fill out the form, take the data of the form (in form of state), and send it by axios, (by preliminary running in the terminal npm install axios) in our case, which works as fetch, but looks nicer. We should specify the name-s in our form, and they should match to the keys elements of state object. handleChange method only grabs the input data of the form and updates the state (this.setState). The main and most important method here that interacts with our Rails part, is handleSubmit, which takes full advantage of axios. The latter takes HTTP request type (post) along with three parameters, the actual address, the updated object, and another object, where credentials should be indicated as true. The POST request must match the exact back-end address (“http://localhost:3000/registrations”), where we send the user object with email and password (actually, user: {email, password} is syntactic sugar, it should be user: {email: email, password: password}, where the values are actually not email and password, but this.state.email and this.state.password. It’s all made possible firstly by destructuring and, secondly, by writing object property value shorthand in JavaScript, for instance, if the object key and value are similar, we can write it one once, instead of email: email, we can write email). After the successful response (hopefully), we will chain the response by then method, which will invoke our handleAuth method from (from Home component).

This is how we create a brand new user and authorize it to enter and enjoy the properties of a web page, which would be impossible to enter otherwise.

Conclusion

It is important to understand the workflow between React controlled components and Rails database, thus making sure that we have a fully functioning login/registration form which will not break down with unpleasant surprises. This is a round trip to our favorite destinations without delays and landing on another country’s territory! The value of these forms cannot be overestimated while most of the web sites benefit from them every second even as we speak.

--

--