Last updated on Sep 9, 2020 by Suraj Sharma
In this article, you are going to learn how you can create a React login form using useReducer
hook and Typescript.
I am using React Material UI for the styling of login form
Let's dive in.
First, create an empty react typescript project using create-react-app
npx create-react-app react-login-form --template typescript
then, inside src folder create a Login.tsx
file
The Login component will use a reducer to manage the local state so keeping that in mind you can define an initial state and a reducer.
type State = {
username: string
password: string
isButtonDisabled: boolean
helperText: string
isError: boolean
};
const initialState:State = {
username: '',
password: '',
isButtonDisabled: true,
helperText: '',
isError: false
};
type Action = { type: 'setUsername', payload: string }
| { type: 'setPassword', payload: string }
| { type: 'setIsButtonDisabled', payload: boolean }
| { type: 'loginSuccess', payload: string }
| { type: 'loginFailed', payload: string }
| { type: 'setIsError', payload: boolean };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'setUsername':
return {
...state,
username: action.payload
};
case 'setPassword':
return {
...state,
password: action.payload
};
case 'setIsButtonDisabled':
return {
...state,
isButtonDisabled: action.payload
};
case 'loginSuccess':
return {
...state,
helperText: action.payload,
isError: false
};
case 'loginFailed':
return {
...state,
helperText: action.payload,
isError: true
};
case 'setIsError':
return {
...state,
isError: action.payload
};
}
}
The above code defines the initial State of the Login Component and a reducer, which returns an immutable state for every dispatched action.
For this tutorial I’m using React Material UI components for the styling of the login form, feel free to use any other library of your choice.
Install the @material-ui/core
npm install --save @material-ui/core
After the installation is complete, define a Login Component
and create two input fields, one for a
username and another for the password and a Submit button.
const Login = () => {
const classes = useStyles();
const [state, dispatch] = useReducer(reducer, initialState);
return (
<form className={classes.container} noValidate autoComplete="off">
<Card className={classes.card}>
<CardHeader className={classes.header} title="Login App" />
<CardContent>
<div>
<TextField
error={state.isError}
fullWidth
id="username"
type="email"
label="Username"
placeholder="Username"
margin="normal"
onChange={handleUsernameChange}
onKeyPress={handleKeyPress}
/>
<TextField
error={state.isError}
fullWidth
id="password"
type="password"
label="Password"
placeholder="Password"
margin="normal"
helperText={state.helperText}
onChange={handlePasswordChange}
onKeyPress={handleKeyPress}
/>
</div>
</CardContent>
<CardActions>
<Button
variant="contained"
size="large"
color="secondary"
className={classes.loginBtn}
onClick={handleLogin}
disabled={state.isButtonDisabled}>
Login
</Button>
</CardActions>
</Card>
</form>
);
}
export default Login;
In the above piece of code you have created the form and assigned the initialState and the reducer to the component, which allows the Login component's methods to manage state using dispatch actions.
In the code above, you have breaking changes, the methods are not yet defined in the component. You can define the methods like this
//triggers when login button is clicked
const handleLogin = () => {
if (state.username === 'abc@email.com' && state.password === 'password') {
dispatch({
type: 'loginSuccess',
payload: 'Login Successfully'
});
} else {
dispatch({
type: 'loginFailed',
payload: 'Incorrect username or password'
});
}
};
//method to submit form when a user presses the Return Key
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.keyCode === 13 || event.which === 13) {
state.isButtonDisabled || handleLogin();
}
};
//listens to change event on username textField and dispatches
the setUsername action.
const handleUsernameChange: React.ChangeEventHandler<HTMLInputElement> =
(event) => {
dispatch({
type: 'setUsername',
payload: event.target.value
});
};
//listens to change event on password textField and dispatches
the setPassword action.
const handlePasswordChange: React.ChangeEventHandler<HTMLInputElement> =
(event) => {
dispatch({
type: 'setPassword',
payload: event.target.value
});
}
Here's the complete code to create a react.js login form with Typescript and react hooks.
import React, { useReducer, useEffect } from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import TextField from '@material-ui/core/TextField';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import CardHeader from '@material-ui/core/CardHeader';
import Button from '@material-ui/core/Button';
const useStyles = makeStyles((theme: Theme) =>
createStyles({
container: {
display: 'flex',
flexWrap: 'wrap',
width: 400,
margin: `${theme.spacing(0)} auto`
},
loginBtn: {
marginTop: theme.spacing(2),
flexGrow: 1
},
header: {
textAlign: 'center',
background: '#212121',
color: '#fff'
},
card: {
marginTop: theme.spacing(10)
}
})
);
//state type
type State = {
username: string
password: string
isButtonDisabled: boolean
helperText: string
isError: boolean
};
const initialState:State = {
username: '',
password: '',
isButtonDisabled: true,
helperText: '',
isError: false
};
type Action = { type: 'setUsername', payload: string }
| { type: 'setPassword', payload: string }
| { type: 'setIsButtonDisabled', payload: boolean }
| { type: 'loginSuccess', payload: string }
| { type: 'loginFailed', payload: string }
| { type: 'setIsError', payload: boolean };
const reducer = (state: State, action: Action): State => {
switch (action.type) {
case 'setUsername':
return {
...state,
username: action.payload
};
case 'setPassword':
return {
...state,
password: action.payload
};
case 'setIsButtonDisabled':
return {
...state,
isButtonDisabled: action.payload
};
case 'loginSuccess':
return {
...state,
helperText: action.payload,
isError: false
};
case 'loginFailed':
return {
...state,
helperText: action.payload,
isError: true
};
case 'setIsError':
return {
...state,
isError: action.payload
};
}
}
const Login = () => {
const classes = useStyles();
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
if (state.username.trim() && state.password.trim()) {
dispatch({
type: 'setIsButtonDisabled',
payload: false
});
} else {
dispatch({
type: 'setIsButtonDisabled',
payload: true
});
}
}, [state.username, state.password]);
const handleLogin = () => {
if (state.username === 'abc@email.com' && state.password === 'password') {
dispatch({
type: 'loginSuccess',
payload: 'Login Successfully'
});
} else {
dispatch({
type: 'loginFailed',
payload: 'Incorrect username or password'
});
}
};
const handleKeyPress = (event: React.KeyboardEvent) => {
if (event.keyCode === 13 || event.which === 13) {
state.isButtonDisabled || handleLogin();
}
};
const handleUsernameChange: React.ChangeEventHandler<HTMLInputElement> =
(event) => {
dispatch({
type: 'setUsername',
payload: event.target.value
});
};
const handlePasswordChange: React.ChangeEventHandler<HTMLInputElement> =
(event) => {
dispatch({
type: 'setPassword',
payload: event.target.value
});
}
return (
<form className={classes.container} noValidate autoComplete="off">
<Card className={classes.card}>
<CardHeader className={classes.header} title="Login App" />
<CardContent>
<div>
<TextField
error={state.isError}
fullWidth
id="username"
type="email"
label="Username"
placeholder="Username"
margin="normal"
onChange={handleUsernameChange}
onKeyPress={handleKeyPress}
/>
<TextField
error={state.isError}
fullWidth
id="password"
type="password"
label="Password"
placeholder="Password"
margin="normal"
helperText={state.helperText}
onChange={handlePasswordChange}
onKeyPress={handleKeyPress}
/>
</div>
</CardContent>
<CardActions>
<Button
variant="contained"
size="large"
color="secondary"
className={classes.loginBtn}
onClick={handleLogin}
disabled={state.isButtonDisabled}>
Login
</Button>
</CardActions>
</Card>
</form>
);
}
export default Login;
Inside your App.tsx
or wherever required, import the Login component. then run
npm run start
on your terminal
The app will run on your localhost:3000
The above code helps you in creating a React login form with form validations using React, Material UI, React hooks and Typescript.
If you like my content and do not want to miss any of my future articles then, please join my newsletter.
PS. The source code is available here for forks, clones and stars.
Related Solutions
Rate this post
Suraj Sharma is the founder of Future Gen AI Services. He holds a B.Tech degree in Computer Science & Engineering from NIT Rourkela.