React is a front-end JavaScript library that can be used to create interactive user interfaces for your application.
In this tutorial, you will create a to-do application. Your application will need to display the tasks, add new tasks, mark tasks as complete, and remove tasks. These actions will touch upon the four aspects of a CRUD (Create, Read, Update, and Delete) application.
This type of project is often accomplished with Class components, but this application will instead integrate React Hooks. React Hooks allow for functional components to have a state and use lifecycle methods, allowing you to avoid using Class components and have more modular and readable code.
You can check out the completed project on CodeSandbox.
To complete this tutorial, you will need:
First, you need to create a new app. In your terminal window, navigate to the place you would like your new application to be located and type:
- npx create-react-app react-to-do
Note: Prior to React 16.8, you would have had to install the alpha build of React 16.7 to utilize React Hooks. At the time of this writing, Create React App will install the latest stable version of React (16.13.1) which supports Hooks.
Next, navigate into the new project directory:
- cd react-to-do
Then, run the project:
- npm start
Navigate to localhost:3000
in your browser to see the spinning React logo.
Your application has now been set-up and you can continue on to building the rest of the app.
Styling will not be the focus of this tutorial, but it will help display the to-do tasks.
Open App.css
in your code editor:
- nano src/App.css
Replace the content of this file with the three classes you will be using throughout your app:
.app {
background: #209cee;
height: 100vh;
padding: 30px;
}
.todo-list {
background: #e8e8e8;
border-radius: 4px;
max-width: 400px;
padding: 5px;
}
.todo {
align-items: center;
background: #fff;
border-radius: 3px;
box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.15);
display: flex;
font-size: 12px;
justify-content: space-between;
margin-bottom: 6px;
padding: 3px 10px;
}
This creates CSS classes for app
, todo-list
, and todo
. It takes advantage of vh
(viewport height) units and flexbox
properties (align-items
and justify-content
).
Styling is complete. Now, you can implement the aspects of CRUD.
Let’s start on the Read part of CRUD. You will want to make a list of things so that you can read and view the list.
A to-do application using classes would resemble something like this:
class App extends Component {
state = {
todos: [
{ text: "Learn about React" },
{ text: "Meet friend for lunch" },
{ text: "Build really cool todo app" }
]
}
setTodos = todos => this.setState({ todos });
render() {
return <div></div>
}
}
You are going to be using React Hooks, so state will look a little different than if you used classes.
Open App.js
:
- nano src/App.js
Modify this file to add the following lines code to App
component:
import React from 'react';
import logo from './logo.svg';
import './App.css';
function App() {
const [todos, setTodos] = React.useState([
{ text: "Learn about React" },
{ text: "Meet friend for lunch" },
{ text: "Build really cool todo app" }
]);
return (
// ...
);
}
export default App;
The component is a functional component. In past versions of React, functional components were unable to handle state, but now, by using Hooks, they can.
todos
, is what you are going to name your state.setTodos
, is what you are going to use to set the state.The hook of useState
is what React uses to hook into the state or lifecycle of the component. You will then create an array of objects and you will have the beginnings of your state.
You will want to create a component that you can use later on in the return
of the main App
component. You will call that Todo
and it will pass in the todo
and show the text
part of the todo (todo.text
).
Revisit App.js
and add the new Todo
component before the App
component:
import React from 'react';
import logo from './logo.svg';
import './App.css';
function Todo({ todo }) {
return (
<div className="todo">
{todo.text}
</div>
);
};
function App() {
// ...
}
export default App;
Let’s create a list of items.
Revisit App.js
and replace the contents of the return
with these new lines of code:
function App() {
// ...
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
/>
))}
</div>
</div>
);
}
By using the JavaScript method, map()
, you will be able to create a new array of items by mapping over the todo
items from state and displaying them by index.
This adds a <div>
for app
, a <div>
for todo-list
, and a map of the todos
to Todo
components.
At this point, it is also possible to remove the logo.svg
as it will no longer be used.
The entire src/App.js
file will resemble this so far:
import React from "react";
import "./App.css";
function Todo({ todo }) {
return (
<div className="todo">
{todo.text}
</div>
);
};
function App() {
const [todos, setTodos] = React.useState([
{ text: "Learn about React" },
{ text: "Meet friend for lunch" },
{ text: "Build really cool todo app" }
]);
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
/>
))}
</div>
</div>
);
}
export default App;
Open your application in a web browser. There will be three to-do items displayed:
You are now reading data and can continue on to the other aspects of CRUD.
Now, let’s give your application the power to create a new item for your to-do app.
While in the App.js
file, you will need to add a couple of things.
First, you will add another component called TodoForm
. In this component you want to:
To set your state, you will write it like so:
const [value, setValue] = React.useState("");
The first is the “value” and the second is how you are going to be setting the state. The state starts off empty, and as you add things to your state, it will add it to your list of to-do items.
You will want to add in a handleSubmit
variable that can handle your addTodo
function and add the item to the list. If nothing is in the input box and the user presses ENTER
, you want it to not add in an empty item to the list.
Add the functionality into a form that has an input box:
// ...
function TodoForm({ addTodo }) {
const [value, setValue] = React.useState("");
const handleSubmit = e => {
e.preventDefault();
if (!value) return;
addTodo(value);
setValue("");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
className="input"
value={value}
onChange={e => setValue(e.target.value)}
/>
</form>
);
}
function App() {
// ...
}
// ...
Add this new TodoForm
component to your App
component:
function App() {
// ...
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
/>
))}
<TodoForm addTodo={addTodo} />
</div>
</div>
);
}
Let’s build the addTodo
function now.
Staying within App.js
, under the state of the App
component, the function will be able to grab the existing list of items, add on the new item, and display that new list.
function App() {
// ...
const addTodo = text => {
const newTodos = [...todos, { text }];
setTodos(newTodos);
};
return(
// ...
);
}
Notice that there is no this.state
. With the new React Hooks, you will have no use for this.state
since the new Hooks understand that it is going to be implied in certain places.
There is a spread operator in the code as well. The three dots before the todos
copy the list for you so that you are able to add on the new to-do item. Then using the keyword that you set earlier, you will set the state with setTodos
.
Open your application in a web browser. There should be three to-do items displayed. There should also be a field for adding new to-do items:
You are now creating data and can continue on to the other aspects of CRUD.
Let’s add the functionality to cross off an item on your to-do list when they are completed.
The state in your App
component needs a little extra for the “Completed” status to be able to change. You will be adding in another key-value pair to your list of objects.
By adding in an isCompleted: false
value, you will set that to false
to begin with and will, when prompted, change that to true
.
Revisit App.js
and add isCompleted
to your state:
function App() {
const [todos, setTodos] = React.useState([
{
text: "Learn about React",
isCompleted: false
},
{
text: "Meet friend for lunch",
isCompleted: false
},
{
text: "Build really cool todo app",
isCompleted: false
}
]);
// ...
}
You will need a function like the addTodo
function, but this one will be able to mark an item as “Complete”. You will do similar things that you did in addTodo
, like using the spread operator to grab the current list of items. In this function, you will change the isCompleted
status to true
so that it knows it is complete. It will then update the state and set the state to the newTodos
.
Update your code with the following:
function App() {
// ...
const completeTodo = index => {
const newTodos = [...todos];
newTodos[index].isCompleted = true;
setTodos(newTodos);
};
return (
// ...
)
}
By using completeTodo
in the Todo
function, you can use that functionality. When the Complete button is clicked, it will add in the textDecoration: line-through
styling and cross out the item.
You will use a ternary operator to complete an item and update the list:
function Todo({ todo, index, completeTodo }) {
return (
<div
className="todo"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
<div>
<button onClick={() => completeTodo(index)}>Complete</button>
</div>
</div>
);
}
Add completeTodo
in the Todo
part of returning the App
component:
function App() {
// ...
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
completeTodo={completeTodo}
/>
))}
<TodoForm addTodo={addTodo} />
</div>
</div>
);
}
Open your application in a web browser. There will be three to-do items displayed. There will also be a Complete button for marking to-do items as complete.
You are now updating data and can continue on to the last aspects of CRUD.
Let’s add the functionality to delete an item on your to-do list when they are removed.
You will build the removeTodo
function so that when you click on an X to delete an item, the item will be deleted. That function will be located by the others underneath the state of the App
component;
function App() {
// ...
const removeTodo = index => {
const newTodos = [...todos];
newTodos.splice(index, 1);
setTodos(newTodos);
};
return (
// ...
)
}
In this removeTodo
function, you will again use the spread operator, but once you grab that current list, you will be splicing the chosen index off of the array of items. Once that is removed, you will return the new state by setting it with setTodos
to be newTodos
.
In your Todo
function, you will want to add in a button to remove the to-do item:
function Todo({ todo, index, completeTodo, removeTodo }) {
return (
<div
className="todo"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
<div>
<button onClick={() => completeTodo(index)}>Complete</button>
<button onClick={() => removeTodo(index)}>x</button>
</div>
</div>
);
}
Add removeTodo
in the Todo
part of returning the App
component:
function App() {
// ...
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
completeTodo={completeTodo}
removeTodo={removeTodo}
/>
))}
<TodoForm addTodo={addTodo} />
</div>
</div>
);
}
Open your application in a web browser. There will be three to-do items displayed. There will also be an X button for removing to-do items.
You are now deleting data and have implemented all four aspects of CRUD.
After you have put together the Todo
component, the TodoForm
component, and the App
component, your App.js
file will resemble this:
import React from "react";
import "./App.css";
function Todo({ todo, index, completeTodo, removeTodo }) {
return (
<div
className="todo"
style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
>
{todo.text}
<div>
<button onClick={() => completeTodo(index)}>Complete</button>
<button onClick={() => removeTodo(index)}>x</button>
</div>
</div>
);
}
function TodoForm({ addTodo }) {
const [value, setValue] = React.useState("");
const handleSubmit = e => {
e.preventDefault();
if (!value) return;
addTodo(value);
setValue("");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
className="input"
value={value}
onChange={e => setValue(e.target.value)}
/>
</form>
);
}
function App() {
const [todos, setTodos] = React.useState([
{
text: "Learn about React",
isCompleted: false
},
{
text: "Meet friend for lunch",
isCompleted: false
},
{
text: "Build really cool todo app",
isCompleted: false
}
]);
const addTodo = text => {
const newTodos = [...todos, { text }];
setTodos(newTodos);
};
const completeTodo = index => {
const newTodos = [...todos];
newTodos[index].isCompleted = true;
setTodos(newTodos);
};
const removeTodo = index => {
const newTodos = [...todos];
newTodos.splice(index, 1);
setTodos(newTodos);
};
return (
<div className="app">
<div className="todo-list">
{todos.map((todo, index) => (
<Todo
key={index}
index={index}
todo={todo}
completeTodo={completeTodo}
removeTodo={removeTodo}
/>
))}
<TodoForm addTodo={addTodo} />
</div>
</div>
);
}
export default App;
You now have an application for all four aspects of CRUD. Creating to-do items, reading to-do items, updating to-do items, and deleting to-do items.
A to-do app can be a great reminder or starting point when it comes to CRUD in web development. Being able to read information, create new information, update existing information, and delete information can be powerful in any application.
In this tutorial, your created a CRUD To-do list app with React Hooks, which allowed for code to be clear, concise, and straightforward.
If you’d like to learn more about React, check out our React topic page for exercises and programming projects.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Easy to follow tutorial. Thanks Swapnil