We will be learning to create a React todo app using REST API. For the back-end, we will be using the Laravel 8 RESTful APIs. For this todo app, I will be starting with user registration, user login. Then the user will be able to create the task, retrieve the task lists, update and delete the task. I will try to create a simple todo app for demonstration purposes. Later, we can extend its functionality as a project portal. I have already created the RESTful APIs using Laravel 8 with Passport auth for the React Todo app. I will be trying to demonstrate all the basic steps so that you can understand exactly the same. Here, we will be implementing the React Todo App using Laravel 8 REST API. So, let’s create a new app in React.
React Todo App Functionalities
Firstly, let me explain you what we will be creating in this React Todo App.
- User Registration
- User Login
- Create Todo
- Edit Todo
- Update Todo and
- Delete Todo
Every functionality will be performed by the Laravel 8 REST API. I have already created the API for Todo App using Laravel 8 Passport Authentication. So, It is recommended to go through the API part first then come to this post for the API handling in the React js. Here, I used the passport token for the user authentication. The logged user can only create, list, edit, and delete only their own todo. So, in every case, we need the token. The token will identify the logged user and on the basis of that token, the user will be able to perform a certain action. These are the overall functionalities in this React todo app using REST API.
Prerequisites
I assume your system is ready to create an app. If not then you have to install the Node.js >= 10. Once, you are ready, let’s create the new app in React js.
Create React Todo App
For creating the react todo app, I am assuming that you have all the setup of React application. If you are an absolute beginner in React. And you have no idea about the installation then follow up on my previous post on How to Install React in Windows and Linux Step By Step.
Let’s start with the creating the Todo App in Reactjs using Laravel 8 REST API. To create the new app, just open the terminal or command prompt and hit the below command.
npx create-react-app todo-app
The above command will create a new folder and install all the necessary files inside it.
It will take a couple of seconds to create the application. Once the application is crated, go to the todo-app folder by changing the directory.
cd todo-app
Now, run the application using the below command.
npm start
The app will be running on the default port localhost:3000. You will be access it on the browser, so you can check it.
Install Bootstrap and Reactstrap for UI
In order to create an attractive design, we will add Bootstrap and Reactstrap inside our project. So to do that, you will have to install this using the below command.
npm install bootstrap reactstrap
After installing the bootstrap we will import the CSS and JS globally inside the React Todo app. So, in order to use Bootstrap, you will have to import Bootstrap in src/index.js file.
import 'bootstrap/dist/css/bootstrap.min.css';
After importing the bootstrap, we can proceed to the steps for React todo app using rest API. For the API handling in React, I will be using the JavaScript fetch method.
JavaScript Fetch for API Handling in React Todo App
The fetch() method is built into most modern browsers. It doesn’t require any installation to use and access. The fetch is a two-step process when handling JSON data.
- First, to make the actual request.
- Second, to call the .json() method on the response.
Now, we can create components folder for the React Todo app.
Create Components Folder in Todo App
Firstly, create the components folder inside the Todo App folder. We will keep all the component files separately. Hence, we will create a separate folder for all possible components as showing below.
- components
- Header
- Registration
- Login
- InputItem
- TodoItem
- TodoList
- EditTodo
So, your folder structure will look like. I will explain each and every folders to you.
Now, we will move to the implementation of the React Todo app.
Create Components For React Todo App
First will create the components inside the every folders. Here, we will create the following components-
- Header.js– The header component is just for showing the title of the app. Also, it will be showing the logout option on the right side. But, the logout option will be visible only when the user is logged in.
- Registration.js– This will be used for user registration.
- Login.js– The user login functionality will be managed here.
- InputItem.js– This component will be used for taking inputs of todo.
- TodoItem.js– It will show the todo item.
- TodoList.js– Here all the todos will be showing.
- EditTodo.js– This is for editing the todo.
After creating the component files the directory will look like this.
Inside the App.js file we will import all the required components. But, we will route the components in this file only.
For Router we will be using react-router-dom. Hence, it will require to install by using the below command.
npm install --save react-router-dom
After installing the react router dom, we will proceed to the next step.
Add Router in App.js File
Here, we will be adding the router inside the App.js file. So, just add the below code there.
import React, { Component } from "react";
import "./App.css";
import {
BrowserRouter as Router,
Route,
Switch,
} from "react-router-dom";
import Login from "./components/Login/Login";
import Registration from "./components/Registration/Registration";
import Header from "./components/Header/Header";
import InputItem from "./components/InputItem/InputItem";
export default class App extends Component {
render() {
return (
<Router>
<Header />
<Switch>
<Route exact path="/" component={Registration} />
<Route path="/login" component={Login} />
<Route path="/todo" component={InputItem} />
</Switch>
</Router>
);
}
}
Let me explain what I did in the above file.
- Firstly, I have imported the Browser Router, Switch, and Route from the react-router-dom.
- Then, I have imported the created components like Login, Registration, Header, and InputItem.
Add Functionality in Header.js
In the Header.js, we will render the header on a conditional basis if a user is on the signup, and login page then the logout button won’t be visible for the user. If the user is logged In then only the Logout button would be visible.
import React, { Component } from "react";
import { Toolbar, Typography, AppBar, Button } from "@material-ui/core";
export default class Header extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: null,
};
}
todoLogout = () => {
let token = sessionStorage.getItem("token");
var myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${token}`);
var requestOptions = {
method: "GET",
headers: myHeaders,
};
fetch("https://todo.programmingfields.com/api/user/logout", requestOptions)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
window.location.reload();
sessionStorage.clear();
let temp = window.location.origin;
window.location.href = temp + "/login";
}
})
.catch((error) => console.log("error", error));
};
componentDidMount() {
let isLoggedIn = sessionStorage.getItem("isLoggedIn");
this.setState({ isLoggedIn: isLoggedIn });
}
render() {
const { isLoggedIn } = this.state;
let logoutDiv = null;
if (isLoggedIn === "true") {
logoutDiv = (
<AppBar
position="static"
style={{ color: "black", backgroundColor: "#F2AA4CFF" }}
>
<Toolbar
style={{ display: " flex", justifyContent: "space-between" }}
>
<Typography variant="h6">Todo App</Typography>
<Button onClick={this.todoLogout}>Logout</Button>
</Toolbar>
</AppBar>
);
}
if (isLoggedIn === null) {
logoutDiv = (
<AppBar
position="static"
style={{ color: "black", backgroundColor: "#F2AA4CFF" }}
>
<Toolbar
style={{ display: " flex", justifyContent: "space-between" }}
>
<Typography variant="h6">Todo App</Typography>
</Toolbar>
</AppBar>
);
}
return <div>{logoutDiv}</div>;
}
}
Add Functionality For Registration Component
For user registration, we have the Registration.js component so add the below snippet there.
import React, { Component } from "react";
import { TextField, Button } from "@material-ui/core";
import { Link } from "react-router-dom";
import { Container } from "reactstrap";
import "./RegistrationStyle.css";
import showPwd from "../../images/showPwd.png";
import hidePwd from "../../images/hidePwd.png";
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
const theme = createMuiTheme({
palette: {
primary: {
main: "#F2AA4CFF",
},
},
});
export default class Registration extends Component {
state = {
signupData: {
first_name: "",
last_name: "",
phone: "",
email: "",
password: "",
full_name: "",
},
hidden: true,
errMsgFirstName: "",
errMsgLastName: "",
errMsgPhone: "",
errMsgEmail: "",
errMsgPassword: "",
successMsg: "",
error: false,
};
toggleShow = () => {
this.setState({ hidden: !this.state.hidden });
};
onChangeHandler = (e, key) => {
const { signupData } = this.state;
signupData[e.target.name] = e.target.value;
this.setState({ signupData });
};
onSubmitHandler = (e) => {
e.preventDefault();
var formdata = new FormData();
formdata.append("first_name", this.state.signupData.first_name);
formdata.append("last_name", this.state.signupData.last_name);
formdata.append("email", this.state.signupData.email);
formdata.append("password", this.state.signupData.password);
formdata.append("phone", this.state.signupData.phone);
var requestOptions = {
method: "POST",
body: formdata,
};
fetch(
"https://todo.programmingfields.com/api/user/register",
requestOptions
)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
this.setState({
signupData: {
first_name: "",
last_name: "",
password: "",
email: "",
phone: "",
},
errMsgFirstName: "",
errMsgLastName: "",
errMsgPhone: "",
errMsgEmail: "",
errMsgPassword: "",
error: false,
});
}
setTimeout(() => {
this.setState({ successMsg: result.message });
}, 1000);
if (result.status === "error" && result.validation_errors.first_name) {
this.setState({
error: true,
errMsgFirstName: result.validation_errors.first_name[0],
});
}
if (result.status === "error" && result.validation_errors.last_name) {
this.setState({
error: true,
errMsgLastName: result.validation_errors.last_name[0],
});
}
if (result.status === "error" && result.validation_errors.phone) {
this.setState({
error: true,
errMsgPhone: result.validation_errors.phone[0],
});
}
if (result.status === "error" && result.validation_errors.email) {
this.setState({
error: true,
errMsgEmail: result.validation_errors.email[0],
});
}
if (result.status === "error" && result.validation_errors.password) {
this.setState({
error: true,
errMsgPassword: result.validation_errors.password[0],
});
}
})
.catch((error) => {
console.log(error);
});
};
render() {
return (
<Container className="themed-container mt-2" fluid="sm">
<div className="text-center">
<i className="fa fa-2x fa-lock" aria-hidden="true"></i>
<div className="text-color">Signup</div>
<div className="hr"></div>
</div>
<ThemeProvider theme={theme}>
<div className="d-flex justify-content-around mb-5">
<div className="txt-first">
<TextField
error={this.state.error}
name="first_name"
label="First Name"
fullWidth
hintText="Phone"
color="primary"
variant="outlined"
value={this.state.signupData.first_name}
onChange={this.onChangeHandler}
autoFocus
helperText={this.state.errMsgFirstName}
/>
</div>
<div className="txt-last">
<TextField
error={this.state.error}
name="last_name"
label="Last Name"
color="primary"
variant="outlined"
value={this.state.signupData.last_name}
onChange={this.onChangeHandler}
fullWidth
helperText={this.state.errMsgLastName}
/>
</div>
</div>
<div className="signup-wrapper">
<TextField
error={this.state.error}
name="phone"
label="Phone"
type="number"
fullWidth
variant="outlined"
value={this.state.signupData.phone}
onChange={this.onChangeHandler}
onInput={(e) => {
e.target.value = Math.max(0, parseInt(e.target.value))
.toString()
.slice(0, 10);
}}
min={0}
helperText={this.state.errMsgPhone}
/>
<TextField
error={this.state.error}
name="email"
label="Email"
type="email"
fullWidth
variant="outlined"
value={this.state.signupData.email}
onChange={this.onChangeHandler}
helperText={this.state.errMsgEmail}
/>
<div className="show-hide-pwd-wrapper">
<TextField
error={this.state.error}
name="password"
label="Password"
type={this.state.hidden ? "password" : "text"}
fullWidth
variant="outlined"
value={this.state.signupData.password}
onChange={this.onChangeHandler}
helperText={this.state.errMsgPassword}
/>
<img
src={this.state.hidden ? showPwd : hidePwd}
onClick={this.toggleShow}
alt="showPwd"
className="eyeIcon"
/>
</div>
<div class=" alert-success pl-5">{this.state.successMsg}</div>
<Button
variant="contained"
fullWidth
color="primary"
onClick={this.onSubmitHandler}
>
SIGN UP
</Button>
<p className="already-txt ml-5">
Already have an account?
<Link to="/login" className="sign-in-txt">
Sign In
</Link>
</p>
</div>
</ThemeProvider>
</Container>
);
}
}
Also, we will put some CSS for styling the register component. Keep this CSS file in the same folder (Register). Also for styling, I am using the material UI for making user interface more attractive.
.bg-color{
background-color: #C0C0C0;
}
.btn-container{
width:10%;
display: flex;
align-self: center;
}
.txt-clor{
color: #05445E;
}
.text-style{
font-size:0.5rem;
font-weight: 700;
}
.fa-lock, .text-color, .fa-user-circle-o{
color:#F2AA4CFF;
font-size: 24px;
font-weight: 700;
}
.txt-first{
width:35%
}
.txt-last{
width:35%;
}
.hr{
border: 2px solid;
width: 25%;
margin: 1% auto 2%;
color: #F2AA4CFF;
}
.signup-wrapper{
width: 85%;
margin: 0 auto;
line-height: 5;
}
.already-txt{
color: #F2AA4CFF;
float: right;
margin-top:-30px;
font-size: 12px;
}
.show-hide-pwd-wrapper{
position:relative;
}
.eyeIcon{
width: 25px;
height: 20px;
position: absolute;
top: 18px;
right: 6px;
}
.sign-in-txt,.sign-in-txt:hover{
color: #F2AA4CFF;
font-weight: bold;
}
Now, run the application to see the result. The Registration page will look like this.
Here, we are taking First Name, Last Name, Phone, Email Id, and Password as inputs from the user. Then we will store these fields into their relevant defined state variables. After that, we will pass these variables to the backend through the API which we will fire on the onSubmitHandler on the button click of SIGN UP.
If the response is a success then we will reset the signup fields, and we will display the success message alert. This success message has a timeout for 1 second.
Here, we did the validation at the API end. So, when you will try to register without filling up the required fields, it will through the validation errors.
If the response status is an error, we will show the error message to the user for the validation errors. The validation errors are getting from the API.
Put Functionality in Login Component
In this Login.js component, the user has to enter the email and the password for login to their respective account. After the successful login, the user will redirect to the todo application page. But if Login is invalid, it will throw the error. So, just add the below snippet to the Login.js file.
import React, { Component } from "react";
import TextField from "@material-ui/core/TextField";
import Button from "@material-ui/core/Button";
import { Container } from "reactstrap";
import { Link } from "react-router-dom";
import "./LoginStyle.css";
import showPwd from "../../images/showPwd.png";
import hidePwd from "../../images/hidePwd.png";
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
import { Redirect } from "react-router-dom";
const theme = createMuiTheme({
palette: {
primary: {
main: "#F2AA4CFF",
},
},
});
export default class Login extends Component {
state = {
loginData:{
email: "",
password: "",
},
errMsgEmail:'',
errMsgPassword:'',
hidden: true,
redirect:false,
errMsg:'',
accessToken: "",
error:false
};
toggleShow = () => {
this.setState({ hidden: !this.state.hidden });
};
onChangeHandler = (e) =>{
const {loginData} = this.state;
loginData[e.target.name] = e.target.value;
this.setState({loginData});
}
onSubmitHandler = () =>{
var formdata = new FormData();
formdata.append("email", this.state.loginData.email);
formdata.append("password", this.state.loginData.password);
var requestOptions = {
method: "POST",
body: formdata,
};
fetch(
"https://todo.programmingfields.com/api/user/login",
requestOptions
)
.then((response) => response.json())
.then((result) => {
if(result.status === 'success'){
this.setState({accessToken:result.token})
sessionStorage.setItem('token', this.state.accessToken)
sessionStorage.setItem('userName', this.state.loginData.email)
sessionStorage.setItem('isLoggedIn', true)
}
if(result.status === 'failed'){
this.setState({
errMsg:result.message
})
}
if(result.status === "error"){
this.setState({
error:true,
errMsgEmail:result.validation_errors.email[0],
errMsgPassword:result.validation_errors.password[0]
})
}
if(result.error === false){
this.setState({ redirect:true })
}
})
.catch((error) => {
console.log("errro",error);
});
}
render() {
const isLoggedIn = sessionStorage.getItem('isLoggedIn');
if (this.state.redirect ) {
return <Redirect to="/todo" />;
}
if(isLoggedIn){
return <Redirect to="/todo" />;
}
return (
<Container className="themed-container mt-2" fluid="sm">
<ThemeProvider theme={theme}>
<div className="wrapper">
<div className="text-center">
<i className="fa fa-user-circle-o" aria-hidden="true"></i>
<div className="text-color">Signin</div>
<div className="hr"></div>
</div>
<div className="signin-wrapper">
<TextField
error={this.state.error}
helperText={this.state.loginData.email === ''? this.state.error : this.state.errMsgEmail}
label="Email"
type="text"
name="email"
fullWidth
variant="outlined"
value={this.state.loginData.email}
onChange={this.onChangeHandler}
/>
<div className="show-hide-pwd-wrapper">
<TextField
error={this.state.error}
helperText={this.state.loginData.password ===''? this.state.error : this.state.errMsgPassword }
label="Password"
name="password"
type={this.state.hidden ? "password" : "text"}
fullWidth
variant="outlined"
value={this.state.loginData.password}
onChange={this.onChangeHandler}
/>
<img
src={this.state.hidden ? showPwd : hidePwd}
onClick={this.toggleShow}
alt="showPwd"
className="eyeIcon"
/>
</div>
<p className="errMsgStyl">{this.state.errMsg}</p>
<Button
variant="contained"
fullWidth
color="primary"
onClick={this.onSubmitHandler}
disabled={!this.state.loginData.email || !this.state.loginData.password}
>
SIGN IN
</Button>
<p to="/sign-up" className="dont-have-txt">
Don't have an Account to Signin? <Link to="/" className="signup-txt">SignUp</Link>
</p>
</div>
</div>
</ThemeProvider>
</Container>
);
}
}
Also, create a CSS file in the same folder and name it LoginStyle.css.
.signin-wrapper{
width:85%;
margin:0 auto;
margin-top: 8%;
line-height: 5;
}
.show-hide-pwd-wrapper{
position:relative;
}
.errMsgStyl{
color:red;
}
.eyeIcon{
width: 25px;
height: 20px;
position: absolute;
top: 18px;
right: 6px;
}
.dont-have-txt{
color: #F2AA4CFF;
float: right;
font-size:12px;
margin-top:-30px;
}
.signup-txt, .signup-txt:hover{
font-weight: bold;
color: #F2AA4CFF;
}
After that check the result of Sign in page.
Here, disabling the sign in button unless the user enters the email id and password as input. If user enters the correct email id and password, It will allow the user for successful sign in into the todo application page.
If the given credentials are wrong then the error message will display.
Also, in the validation error message, you will get the errors for each validation separately. Just take a look at the below screenshot. If an email is not a valid email then it will throw an error. Also for the password minimum digit must be 3.
After successful login, the user will redirect to the Todo List. Also, we will store the username(email id), token, and isLoggedIn true in the session storage of the logged in user.
Add Functionality in InputItem Component
The InputItem.js component is used for taking inputs from the user for creating todo. Also for displaying todos list, edit todo, and delete todo. So, add the below snippet in the same component.
import React, { Component } from "react";
import { Button, Container } from "reactstrap";
import TextField from "@material-ui/core/TextField";
import { Redirect } from "react-router-dom";
import TodoList from "../TodoList/TodoList";
import "./style.css";
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
import EditTodo from "../EditTodo/EditTodo";
const theme = createMuiTheme({
palette: {
primary: {
main: "#F2AA4CFF",
},
},
});
export default class InputItem extends Component {
constructor(props) {
super(props);
this.state = {
taskData: {
title: "",
description: "",
status: "",
},
showTaskData: [],
successAlertMsg: "",
todoDeleteMsg: "",
editTaskDataModal: false,
editTaskData: {
title: "",
description: "",
},
successTodoUpdatedMsg: "",
};
}
componentDidMount() {
this.getTaskData();
}
addItem = () => {
let token = sessionStorage.getItem("token");
var formdata = new FormData();
formdata.append("title", this.state.taskData.title);
formdata.append("description", this.state.taskData.description);
var requestOptions = {
method: "POST",
body: formdata,
headers: {
Authorization: `Bearer ${token}`,
},
};
fetch("https://todo.programmingfields.com/api/user/todos", requestOptions)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
this.setState({ successAlertMsg: result.message }, () =>
this.getTaskData()
);
setTimeout(() => {
this.setState({ successAlertMsg: "" });
}, 1000);
}
if (result.error === false) {
this.setState({
taskData: {
title: "",
description: "",
},
});
}
})
.catch((error) => {
console.log(error);
});
};
getTaskData() {
let token = sessionStorage.getItem("token");
var requestOptions = {
method: "GET",
headers: {
Authorization: `Bearer ${token}`,
},
};
fetch("https://todo.programmingfields.com/api/user/todos", requestOptions)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
this.setState({
showTaskData: result.data,
});
}
})
.catch((error) => {
console.log(error);
});
}
onChangehandler = (e) => {
const { taskData } = this.state;
taskData[e.target.name] = e.target.value;
console.log((taskData[e.target.name] = e.target.value));
this.setState({ taskData });
};
clearList = () => {
this.setState({
showTaskData: [],
});
};
handleDelete = (id) => {
let token = sessionStorage.getItem("token");
var requestOptions = {
method: "DELETE",
headers: {
Authorization: `Bearer ${token}`,
},
};
fetch(
"https://todo.programmingfields.com/api/user/todos/" + id,
requestOptions
)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
this.setState(
{
todoDeleteMsg: result.message,
},
() => this.getTaskData()
);
setTimeout(() => {
this.setState({ todoDeleteMsg: "" });
}, 1000);
}
});
};
toggleEditTaskModal = () => {
this.setState({
editTaskDataModal: !this.state.editTaskDataModal,
});
};
onChangeEditTodoHandler = (e) => {
let { editTaskData } = this.state;
editTaskData[e.target.name] = e.target.value;
this.setState({ editTaskData });
};
editTodo = (id, title, description) => {
this.setState({
editTaskData: { id, title, description },
editTaskDataModal: !this.state.editTaskDataModal,
});
};
updateTodo = () => {
let { id, title, description } = this.state.editTaskData;
let token = sessionStorage.getItem("token");
var myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${token}`);
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("title", title);
urlencoded.append("description", description);
var requestOptions = {
method: "PUT",
headers: myHeaders,
body: urlencoded,
};
fetch(
"https://todo.programmingfields.com/api/user/todos/" + id,
requestOptions
)
.then((response) => response.json())
.then((result) => {
if (result.status === "success") {
this.setState(
{
editTaskDataModal: false,
editTaskData: { title, description },
},
() => this.getTaskData()
);
setTimeout(() => {
this.setState({ editTaskDataModal: false });
}, 1000);
}
if (result.errors === false) {
this.setState({
successTodoUpdatedMsg: result.message,
});
}
})
.catch((error) => console.log("error", error));
};
render() {
const { title, description } = this.state.taskData;
if (this.state.isLoggedIn === false) {
return <Redirect to="/log-in" />;
}
return (
<Container className="themed-container mt-5" fluid="sm">
<div className="input-field-container">
<ThemeProvider theme={theme}>
<TextField
type="text"
name="title"
placeholder="Task Title"
value={title}
onChange={this.onChangehandler}
color="primary"
variant="outlined"
/>
<TextField
type="text"
name="description"
placeholder="Task description"
value={description}
onChange={this.onChangehandler}
color="primary"
variant="outlined"
style={{ width: "50%" }}
/>
<Button
color="success"
className="font-weight-bold add-task"
onClick={this.addItem}
>
+
</Button>
</ThemeProvider>
</div>
<div class="text-success p-4 mt-2">{this.state.successAlertMsg}</div>
{/*TODO list */}
<TodoList
showTaskData={this.state.showTaskData}
clearList={this.clearList}
handleDelete={this.handleDelete}
todoDeleteMsg={this.state.todoDeleteMsg}
editTodo={this.editTodo}
toggleEditTaskModal={this.toggleEditTaskModal}
/>
{/* Model for Edit Todo */}
<EditTodo
toggleEditTaskModal={this.toggleEditTaskModal}
editTaskDataModal={this.state.editTaskDataModal}
onChangeEditTodoHandler={this.onChangeEditTodoHandler}
editTodo={this.editTodo}
editTaskData={this.state.editTaskData}
updateTodo={this.updateTodo}
successTodoUpdatedMsg={this.state.successTodoUpdatedMsg}
/>
</Container>
);
}
}
In this component, we will take the todo title and description from the user. Also we will pass the data as props for component <TodoList /> and <EditTodo />
Add Functionality in TodoList Component
Put the below snippet in the TodoList.js component.
import React, { Component } from "react";
import Button from "reactstrap/lib/Button";
import TodoItem from "../TodoItem/TodoItem";
export default class TodoList extends Component {
render() {
const {
showTaskData,
clearList,
handleDelete,
todoDeleteMsg,
editTodo,
} = this.props;
let taskData = [];
if (showTaskData.length) {
taskData = showTaskData.map((task) => {
return (
<TodoItem
key={task.id}
title={task.title}
description={task.description}
handleDelete={() => {
handleDelete(task.id);
}}
todoDeleteMsg={todoDeleteMsg}
editTodo={() => {
editTodo(task.id, task.title, task.description);
}}
/>
);
});
}
return (
<ul className="list-group my-2">
<h3 className="text-capitalize">Todo List </h3>
<div className="d-flex justify-content-between mb-5">
Task and Description
</div>
{taskData}
<Button color="danger" onClick={clearList}>
Clear all
</Button>
<p className="text-danger">{todoDeleteMsg}</p>
</ul>
);
}
}
In the TodoList component, we will show all List of Todo Items. Also, pass the data as props in the <TodoItem /> component.
import React from "react";
const TodoItem = (props) => {
const { title, description, handleDelete, editTodo } = props;
return (
<li className="list-group-item d-flex text-capitalize justify-content-between my-2">
<div className="d-flex">
<b style={{ marginRight: "100px" }}>{title}</b>
<h6>{description}</h6>
</div>
<div className="todo-icons">
<span className="mx-2 text-success" onClick={editTodo}>
<i className="fa fa-pencil" />
</span>
<span className="mx-2 text-danger" onClick={handleDelete}>
<i className="fa fa-trash" />
</span>
</div>
</li>
);
};
export default TodoItem;
After adding the above code, let’s check the result.
Currently, the logged user has no todo. So, I am creating a to-do here. After creating the todo, I got the success response message as you can see in the below result.
Add Functionality For EditTodo Component
In this step, we will put the functionailty for EditTodo.js component.
import React, { Component } from "react";
import { Button } from "reactstrap";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogContentText from "@material-ui/core/DialogContentText";
import DialogTitle from "@material-ui/core/DialogTitle";
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles";
const theme = createMuiTheme({
palette: {
primary: {
main: "#F2AA4CFF",
},
},
});
export default class EditTodo extends Component {
render() {
return (
<div>
<Dialog
fullWidth
open={this.props.editTaskDataModal}
onClose={this.props.onChangeEditTodoHandler}
modal={false}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Update Todo</DialogTitle>
<DialogContent>
<DialogContentText>Todo List</DialogContentText>
<div className="input-field-container">
<ThemeProvider theme={theme}>
<TextField
autoFocus
type="text"
name="title"
placeholder="Task Title"
value={this.props.editTaskData.title}
onChange={this.props.onChangeEditTodoHandler}
className="task-title"
color="primary"
variant="outlined"
style={{ width: "35%" }}
/>
<TextField
type="text"
name="description"
placeholder="Task description"
value={this.props.editTaskData.description}
onChange={this.props.onChangeEditTodoHandler}
color="primary"
variant="outlined"
style={{ width: "60%" }}
/>
</ThemeProvider>
</div>
</DialogContent>
<div class="text-success p-4 mt-2">
{this.props.successTodoUpdatedMsg}
</div>
<DialogActions>
<Button onClick={this.props.toggleEditTaskModal} color="primary">
Cancel
</Button>
<Button
onClick={this.props.updateTodo}
color="success"
className="font-weight-bold add-task"
>
UPDATE
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
I have already added some todo here.
Now, after clicking on the Edit button, a dialog box is opened with the todo title and description.
When you will click on the Update button, the API will be triggered and it will update the todo.
Delete Todo
When you will click on the delete button, it will trigger the API to delete it. You can see the below result. After deleting the Todo, I got the success response as showing below.
User Logout
Lastly, there is the functionality for the User logout. When you will click on the Logout button, the API will be triggered.
By clicking on the Logout Button, the Todo app will be logout and the session storage values userName, token, and IsLoggedIn true will be cleared. After the logout, the user will be redirected to the Login page. Also, this token will detach from the database so next time this token won’t work for this user.
You can download the source code of this React Todo App through the Githubon the above download button.
Conclusion
We created React todo app through the Laravel 8 RESTful APIs. Here, we used JavaScript fetch() methods for API handling in React. For the backend, we used Laravel 8 RESTful APIs with MySQL database. In this Todo app, we implemented the User Signup, Login, Create Todo, Display Todos, Edit Todo, and Delete Todo functionality. So, if you face any issue regarding this post then don’t forget to put your comments below in the comment section. I will try to help you with the possible solution as soon as possible. Thank you.
Leave a Reply