Creating Login and Register in React TypeScript using JWT authentication
Hello, how are you all friends, this time we will discuss logging in and registering again. However, this time we will create using TypeScript.
TypeScript
TypeScript It is one of the most widely used programming languages.
This programming language was designed by Anders Hejlsberg of Microsoft, who is also the designer of the C# programming language. The first version of TypeScript was introduced in 2012.
In addition to programming languages, TypeScript is also a tools. TypeScript encapsulates existing programming languages from JavaScript. In other words, TypeScript is JavaScript with some additional features. TypeScript uses two modules, namely internal and external.
TypeScript’s internal modules are similar to namespace as available in C#. This allows assigning symbols (variables, functions, classes) outside the global scope.
Internal module declarations can be split into multiple files. This encourages the development team to have well-structured projects with small, single-purpose files.
Meanwhile, external modules are TypeScript’s way of defining asynchronously loaded modules for either node.js (commonjs) or require.js (AMD) module loading systems.
TypeScript also uses modules to declare APIs that are exposed by third parties.
This is done through what is called an “internal or external module ambient,” which contains only the code and interface.
This way it is possible to provide the same level of IDE/type checker dukungan untuk kode yang tersedia dalam JavaScript murni seolah-olah itu ditulis dalam TypeScript. Such ambient declarations are stored in a “.d.ts” file.
You can study other tutorials:
REST API Login and Register Node.js with JWT
Automatic Number Format With JavaScript All Countries
JavaScript Tutorial #1 : Introduction to Javascript
What is JWT or JSON Web Token?
JSON Web Token is a modern Internet-level standard for generating data with a voluntary signature and voluntary encryption whose payload carries JSON stating a number of claims. The token has been signed using either a private secret or a public/private key.
Implementation
- Create a new project using React TypeScript.
npx create-react-app auth-typescript --template typescript
- Go to your React Typescript Application Directory and install the bootstrap, axios, react-hook-form, react-toastify & react-router-dom packages with the following commands:
npm install axios bootstrap react-router-dom@5.2.0 react-hook-form react-toastify
- In your App.tsx file, delete all the code and paste the following. Basically, what we’re trying to do is add the react-router-dom functionality to route to pages with different links:
import React from "react";
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.bundle.min.js";
import SignUp from "./Components/SignUp";
import Login from "./Components/Login";
import PrivateRoute from "./Auth/PrivateRoute";
import Home from "./Components/Home";
import {BrowserRouter, Route, Switch} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Switch>
<PrivateRoute exact path="/login" component={Login}/>
<Route exact path="/register" component={SignUp} />
</Switch>
</BrowserRouter>
);
}export default App;
- Now we will create a route for our application. Create a folder called “Auth” and under this folder create a new file known as PrivateRoute.tsx file, copy all the code and paste the following, basically what we are trying to do here is we are removing our JWT Token from local Storage. And check if we don’t have JWT token in our LocalStorage then we will go to Login route, if there is no token it will stay on homepage.
import React from "react";
import { Redirect, Route } from "react-router-dom";const PrivateRoute = (props) => {
const token = localStorage.getItem("auth");
return <>{token ? <Route {...props} /> : <Redirect to="/login" />}</>;
};export default PrivateRoute;
- Now we will create a UI for this. First open the src folder, then inside the src folder create a new folder named “Components”.
And inside components folder create 3 files as:
Login.tsx
SignUp.tsx
Home.tsx
home.css
Before we proceed any further, make sure you have your own JWT Authentication Node API that provides JWT Tokens. Because we use the ones made in our local machines. Please change the URL code http://localhost:4000 according to your API URL code.
Login.tsx
import React, { FC } from "react";
import { Link } from "react-router-dom";
import { useForm } from "react-hook-form";
import axios from "axios";
import { ToastContainer, toast, Flip } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
import { RouteComponentProps } from "react-router";type SomeComponentProps = RouteComponentProps;
const Login: FC<SomeComponentProps> = ({ history }): JSX.Element => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm(); const login = (data: any) => {
let params = {
email: data.email,
password: data.password,
};
axios
.post("http://localhost:3000/api/login", params)
.then(function (response) {
// IF EMAIL ALREADY EXISTS
if (response.data.success === false) {
toast.error(response.data.error, {
position: "top-right",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: false,
progress: 0,
toastId: "my_toast",
});
} else {
toast.success(response.data.message, {
position: "top-right",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: false,
progress: 0,
toastId: "my_toast",
});
localStorage.setItem("auth", response.data.token);
setTimeout(() => {
history.push("/");
}, 3000);
}
}) .catch(function (error) {
console.log(error);
});
}; return (
<>
<div className="container">
<div
className="row d-flex justify-content-center align-items-center"
style={{ height: "100vh" }}
>
<div className="card mb-3" style={{ maxWidth: "320px" }}>
<div className="col-md-12">
<div className="card-body">
<h3 className="card-title text-center text-secondary mt-3">
Login Form
</h3>
<form autoComplete="off" onSubmit={handleSubmit(login)}>
<div className="mb-3 mt-4">
<label className="form-label">Email</label>
<input
type="email"
className="form-control shadow-none"
id="exampleFormControlInput1"
{...register("email", { required: "Email is required!" })}
/>
{errors.email && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.email.message}
</p>
)}
</div>
<div className="mb-3">
<label className="form-label">Password</label>
<input
type="password"
className="form-control shadow-none"
id="exampleFormControlInput2"
{...register("password", {
required: "Password is required!",
})}
/>
{errors.password && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.password.message}
</p>
)}
</div>
<div className="text-center mt-4 ">
<button
className="btn btn-outline-primary text-center shadow-none mb-3"
type="submit"
>
Submit
</button>
<p className="card-text pb-2">
Have an Account?{" "}
<Link style={{ textDecoration: "none" }} to={"/register"}>
Sign Up
</Link>
</p>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar
closeOnClick
rtl={false}
pauseOnFocusLoss={false}
draggable={false}
pauseOnHover
limit={1}
transition={Flip}
/>
</>
);
};
export default Login;
SignUp.tsx
import React, { FC } from "react";
import { Link, RouteComponentProps } from "react-router-dom";
import { useForm } from "react-hook-form";
import axios from "axios";
import { ToastContainer, toast, Flip } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";type SomeComponentProps = RouteComponentProps;
const SignUp: FC<SomeComponentProps> = ({ history }) => {
const {
register,
handleSubmit,
watch,
reset,
formState: { errors },
} = useForm();
const submitData = (data: any) => {
let params = {
firstname: data.firstname,
lastname: data.lastname,
email: data.email,
password: data.password,
confirmpassword: data.cpassword,
};
console.log(data);
axios
.post("http://localhost:3000/api/signup", params)
.then(function (response) {
toast.success(response.data.message, {
position: "top-right",
autoClose: 3000,
hideProgressBar: true,
closeOnClick: true,
pauseOnHover: true,
draggable: false,
progress: 0,
toastId: "my_toast",
});
reset();
setTimeout(() => {
history.push("/login");
}, 3000);
}) .catch(function (error) {
console.log(error);
});
};
return (
<>
<div className="container">
<div
className="row d-flex justify-content-center align-items-center"
style={{ height: "100vh" }}
>
<div className="card mb-3 mt-3 rounded" style={{ maxWidth: "500px" }}>
<div className="col-md-12">
<div className="card-body">
<h3 className="card-title text-center text-secondary mt-3 mb-3">
Sign Up Form
</h3>
<form
className="row"
autoComplete="off"
onSubmit={handleSubmit(submitData)}
>
<div className="col-md-6">
<div className="">
<label className="form-label">Firstname</label>
<input
type="text"
className="form-control form-control-sm"
id="exampleFormControlInput1"
{...register("firstname", {
required: "Firstname is required!",
})}
/>
{errors.firstname && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.firstname.message}
</p>
)}
</div>
</div>
<div className="col-md-6">
<div className="">
<label className="form-label">Lastname</label>
<input
type="text"
className="form-control form-control-sm"
id="exampleFormControlInput2"
{...register("lastname", {
required: "Lastname is required!",
})}
/>
{errors.lastname && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.lastname.message}
</p>
)}
</div>
</div> <div className="">
<label className="form-label">Email</label>
<input
type="email"
className="form-control form-control-sm"
id="exampleFormControlInput3"
{...register("email", { required: "Email is required!" })}
/>
{errors.email && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.email.message}
</p>
)}
</div>
<div className="">
<label className="form-label">Password</label>
<input
type="password"
className="form-control form-control-sm"
id="exampleFormControlInput5"
{...register("password", {
required: "Password is required!",
})}
/>
{errors.password && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.password.message}
</p>
)}
</div>
<div>
<label className="form-label">Confirm Password</label>
<input
type="password"
className="form-control form-control-sm"
id="exampleFormControlInput6"
{...register("cpassword", {
required: "Confirm Password is required", validate: (value) =>
value === watch("password") ||
"Passwords don't match.",
})}
/>
{errors.cpassword && (
<p className="text-danger" style={{ fontSize: 14 }}>
{errors.cpassword.message}
</p>
)}
</div>
<div className="text-center mt-4 ">
<button
className="btn btn-outline-primary text-center shadow-none mb-3"
type="submit"
>
Submit
</button>
<p className="card-text">
Already have an account?{" "}
<Link style={{ textDecoration: "none" }} to={"/login"}>
Log In
</Link>
</p>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
<ToastContainer
position="top-right"
autoClose={5000}
hideProgressBar
closeOnClick
rtl={false}
pauseOnFocusLoss={false}
draggable={false}
pauseOnHover
limit={1}
transition={Flip}
/>
</>
);
};export default SignUp;
Home.tsx
import React, { FC } from "react";
import { RouteComponentProps } from "react-router-dom";
import "./home.css";type SomeComponentProps = RouteComponentProps;
const Home: FC<SomeComponentProps> = ({ history }) => {
const logout = () => {
localStorage.clear();
history.push("/login");
};
return (
<>
<div
style={{
display: "flex",
justifyContent: "space-between",
paddingLeft: 50,
paddingRight: 50,
}}
>
<div>
<h3 className="m-3">Home</h3>
</div>
<div>
<button type="submit" className="buttons" onClick={logout}>
Logout
</button>
</div>
</div>
<div className="container">
<div
className="row d-flex justify-content-center align-items-center text-center"
style={{ height: "100vh" }}
>
<p className="muted display-6">Hello User👋</p>
</div>
</div>
</>
);
};export default Home;
home.css
.buttons
{
color: white;
background: cornflowerblue;
padding: 10px 40px;
margin: 10px;
border: none;
border-radius: 30px;
cursor: pointer;
}
- Now execute the code by following command:
npm start
That’s the tutorial this time that I can convey, hopefully it will be useful.
Thank you…
source: