Hi guys, Today we gonna implement authentication in React. We gonna build beautiful forms and handle APIs. Back-end of this project is already built, download it from below post.
Node.js API Authentication with JWT (Json Web Token) - Auth Middleware
For better understanding watch Demo Video & Support
Source code of this project
Let's Start Coding...
Create React App & Install dependencies
$ npx create-react-app react-auth-jwt
$ cd react-auth-jwt
$ npm install react-router-dom axios @material-ui/core jwt-decode
$ npm start
react-router-dom : A tool that allows you to handle routes in a web app
axios : It's a promise based HTTP client for the browser and Node.js
@material-ui/core (optional) : Makes writing beautiful UI easy
jwt-decode : It helps decoding JWT token
Configure Environmental Variables
/.env
REACT_APP_API_URL = http://localhost:8080/api
Configure Auth Service
/services/authServices.js
import axios from "axios";
import jwtDecode from "jwt-decode";
const apiUrl = process.env.REACT_APP_API_URL;
export function login(data) {
return axios.post(`${apiUrl}/auth`, data);
}
export function getCurrentUser() {
try {
const token = localStorage.getItem("token");
return jwtDecode(token);
} catch (error) {
return null;
}
}
export function logout() {
localStorage.removeItem("token");
}
Configure User Service
/services/userServices.js
import axios from "axios";
const apiUrl = process.env.REACT_APP_API_URL;
export function register(data) {
return axios.post(`${apiUrl}/users`, data);
}
App.css
a {
text-decoration: none;
}
.flex {
display: flex;
justify-content: center;
align-items: center;
}
.column {
flex-direction: column;
}
.full_screen {
width: 100vw;
height: 100vh;
}
.form {
display: flex;
flex-direction: column;
width: 300px;
padding: 20px;
}
.form_heading {
font-size: 25px;
font-weight: bold;
text-align: center;
margin-bottom: 15px;
}
.input {
width: 100% !important;
margin: 5px 0 !important;
}
Home Component
/src/components/Home.jsx
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { getCurrentUser } from "../services/authServices";
import { AppBar, Toolbar, Button } from "@material-ui/core";
const Home = () => {
const [user, setUser] = useState("");
useEffect(() => {
setUser(getCurrentUser());
}, []);
return (
<AppBar color="default">
<Toolbar>
<h3 style={{ flexGrow: "1" }}>Domain</h3>
{!user && (
<React.Fragment>
<Link to="/login">
<Button
style={{ marginRight: "10px" }}
variant="outlined"
color="secondary"
>
Login
</Button>
</Link>
<Link to="/signup">
<Button variant="outlined" color="secondary">
Signup
</Button>
</Link>
</React.Fragment>
)}
{user && (
<React.Fragment>
<h4 style={{ marginRight: "15px" }}>{user.name}</h4>
<Link to="/logout">
<Button variant="outlined" color="secondary">
Logout
</Button>
</Link>
</React.Fragment>
)}
</Toolbar>
</AppBar>
);
};
export default Home;
Input Component
/src/components/common/Input.jsx
import React from "react";
import { TextField } from "@material-ui/core";
const Input = ({ error, ...rest }) => {
return (
<React.Fragment>
{error ? (
<TextField
{...rest}
error
helperText={error}
size="small"
variant="outlined"
className="input"
/>
) : (
<TextField
{...rest}
variant="outlined"
size="small"
className="input"
/>
)}
</React.Fragment>
);
};
export default Input;
Form Component
/src/components/common/Form.js
import React, { Component } from "react";
import { Button } from "@material-ui/core";
import Input from "./Input";
class Form extends Component {
state = { data: {}, errors: {} };
handleChange = ({ currentTarget: input }) => {
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data });
};
handleSubmit = (event) => {
event.preventDefault();
this.doSubmit();
};
renderInput(name, label, type = "text", required = true) {
const { data, errors } = this.state;
return (
<Input
name={name}
label={label}
type={type}
required={required}
value={data[name]}
error={errors[name]}
onChange={this.handleChange}
/>
);
}
renderSubmitBtn(name) {
return (
<Button
type="submit"
style={{ marginLeft: "auto" }}
variant="outlined"
size="medium"
color="secondary"
>
{name}
</Button>
);
}
}
export default Form;
Signup Component
/src/components/Signup.jsx
import React from "react";
import { Link } from "react-router-dom";
import { register } from "../services/userServices";
import { Paper } from "@material-ui/core";
import Form from "./common/Form";
class Signup extends Form {
state = { data: { name: "", email: "", password: "" }, errors: {} };
doSubmit = async () => {
try {
await register(this.state.data);
this.props.history.push("/login");
} catch (error) {
console.log(error);
}
};
render() {
return (
<form
onSubmit={this.handleSubmit}
className="full_screen flex column"
>
<Paper elevation={3} className="form">
<div className="form_heading">Signup</div>
{this.renderInput("name", "Name")}
{this.renderInput("email", "Email", "email")}
{this.renderInput("password", "Password", "password")}
{this.renderSubmitBtn("Signup")}
</Paper>
<div style={{ margin: "10px 0" }}>
Already have an account? <Link to="/login">Login</Link>
</div>
</form>
);
}
}
export default Signup;
Login Component
/src/components/Login.jsx
import React from "react";
import { Link } from "react-router-dom";
import { login } from "../services/authServices";
import { Paper } from "@material-ui/core";
import Form from "./common/Form";
class Login extends Form {
state = { data: { email: "", password: "" }, errors: {} };
doSubmit = async () => {
try {
const { data } = await login(this.state.data);
window.localStorage.setItem("token", data);
window.location = "/";
} catch (error) {
const errors = { ...this.state.errors };
errors.email = error.response.data;
errors.password = error.response.data;
this.setState({ errors });
}
};
render() {
return (
<form
onSubmit={this.handleSubmit}
className="full_screen flex column"
>
<Paper className="form" elevation={3}>
<div className="form_heading">Login</div>
{this.renderInput("email", "Email", "email")}
{this.renderInput("password", "Password", "password")}
{this.renderSubmitBtn("Login")}
</Paper>
<div style={{ margin: "10px 0" }}>
Don't have an account? <Link to="/signup">Signup</Link>
</div>
</form>
);
}
}
export default Login;
Logout Component
/src/components/Logout.jsx
import { useEffect } from "react";
import { logout } from "../services/authServices";
const Logout = () => {
useEffect(() => {
logout();
window.location = "/";
}, []);
return null;
};
export default Logout;
React Router Dom
In Index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
In App.js
import {Switch, Route} from 'react-router-dom';
import Home from './components/Home';
import Signup from './components/Signup';
import Login from './components/Login';
import Logout from './components/Logout';
import "./App.css";
function App() {
return (
<Switch>
<Route path="/" exact component={Home} />
<Route path="/signup" component={Signup} />
<Route path="/login" component={Login} />
<Route path="/logout" component={Logout} />
</Switch>
);
}
export default App;
That's it Run on local server and test app. If you found any mistakes or making code better please let me know in comment. I hope you have learned something.
Thank you...
Top comments (0)