Devise with React, Webpacker, and Rails

Jane HW
3 min readJan 3, 2018

Devise is one of the most popular authentication solutions for Rails because of its strong versatility, clear documentation, and large community. However, I couldn’t find any tutorials or documentation on integrating it with React on the Webpacker gem, so I decided to lay out my own solution.

I am going to assume you can set up a Rails app with React through the Webpacker gem on your own, and that you have Devise already installed with a user model generated, migrated, and ready for action. I am also going to assume you have a basic understanding of React and the React lifecycle.

First, you are going to want a container component. Most likely this will be your App.jsx file because you will want to pass down information about the current user to all of your other components through props. We will be using Axios, so make sure you install that on yarn before we begin.

class App extends React.Component {constructor(){
super();
this.state = {
currentUser: null
}
this.updateCurrentUser = this.updateCurrentUser.bind(this);
}
componentDidMount(){
let that = this
axios.get('/users/check_for_user',{
})
.then(function(response){
if(response.data.email){
that.setState({
currentUser: response.data.email
})
} else {
that.setState({
currentUser: null
})
}
})
.catch(function(error){
console.log(error);
})
}
updateCurrentUser(email) {
this.setState({
currentUser: email
})
}
render(){
return (
<div>
<Header updateCurrentUser={this.updateCurrentUser}/>
</div>
)
}
}

In this component, we have set up the state to default to null without a user.

Then we have a function that will make an Axios call to a custom route that checks if a user is logged in or not. In my Rails controller I send a JSON version of the Devise current_user method if someone is logged in and an empty user object if someone is not. Putting this in the DidMount phase of the React lifecycle allows the user to remain logged in even after the page is refreshed. This call is what allows you to use Devise with React instead of passing around tokens.

We have an updateCurrentUser function that we will not actually be using in this container, but I put it in this container because it will be used in multiple components so it will be easy to access through props later on if it stays here.

Header.jsx

class Header extends React.Component {constructor(props){
super(props);
if (this.props.currentUser == null){
this.state = {
page:"login"
}
} else{
this.state = {
page: "delete"
}
}
this.changePage = this.changePage.bind(this);
}
changePage(newPage) {
this.setState({
page: newPage
})
}
render() {
switch(this.state.page) {
case "signup":
return <Signup changePage={this.changePage} updateCurrentUser={this.props.updateCurrentUser}/>
case "login":
return <Login changePage={this.changePage} updateCurrentUser={this.props.updateCurrentUser}/>
case "delete":
return <Logout changePage={this.changePage} updateCurrentUser={this.props.updateCurrentUser}/>
}
}
}

In this component I use switch to change which components are rendered. This has less to do with authentication and more to do with style, so I will continue on to the Signup.jsx component:

Signup.jsx

class Signup extends React.Component {
constructor(props){
super(props);
this.handleSignup = this.handleSignup.bind(this);
}
handleSignup(e) {
e.preventDefault();
let that = this
axios.post('/users', {
user: {
email: document.getElementById("email").value,
password: document.getElementById("password").value,
password_confirmation: document.getElementById("password_confirmation").value
}
})
.then(function(response){
that.props.changePage("delete");
that.props.updateCurrentUser(email);
})
.catch(function(error){
console.log(error)
})
}render() {
return (
<div>
<h2>Signup</h2>
<form>
<input id="email" placeholder="email"/>
<input id="password" placeholder="password"/>
<input id="password_confirmation" placeholder="retype password"/>
<button onClick={this.handleSignup}>Submit</button>
</form>
<button onClick={() => this.props.changePage("login")}>Back to Login</button>
</div>
);
};
};

In this component we make another Axios call, taking information directly from the form rendered on the page. Devise fills in most of the backend for you, so I will assume you can figure out how to send things in that order.

The Login looks the same as Signup, so I will finish with…

Logout.jsx

class Logout extends React.Component {constructor(props){
super(props);
this.handleLogout = this.handleLogout.bind(this);
}
handleLogout(e) {
e.preventDefault();
let that = this
let email = this.props.currentUser
axios.delete('/users/sign_out', {
})
.then(function(response){
that.props.changePage("login")
})
.catch(function(error){
console.log(error)
})
}
render() {
return (
<button onClick={this.handleLogout}>Sign Out</button>
);
};
}

As you can see, integrating Devise through Webpacker is not too difficult for a fast, easy authentication system when you don’t want to mess with passing tokens around React.

📝 Read this story later in Journal.

👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.

--

--