DEV Community

Cover image for How to build a React CRUD todo app (create/read todos)
Joseph Lynn
Joseph Lynn

Posted on • Updated on

How to build a React CRUD todo app (create/read todos)

In this series, we will build a todo application.

To begin, we will go over a very basic way to build this application and revise as we gain more knowledge.

I suggest following along and if you get stuck, you can fork the code from the Code Sandbox

1. Set the initial state

Lets start with creating a couple state values.

import { useState } from "react";
import "./styles.css";

export default function App() {
  // need state to keep track of todos
  const [todos, setTodos] = useState([]);
  // need state to keep track of the value in the input
  const [todo, setTodo] = useState("");

  return (
    <div className="App">
      <h1>Todo App</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

2. Build the JSX

Lets build out the skeleton of what we want to see on the screen.

import { useState } from "react";
import "./styles.css";

export default function App() {
  // need a state to keep track of todos
  const [todos, setTodos] = useState([]);
  // need state to keep track of the value in the input
  const [todo, setTodo] = useState("");

  return (
    <div className="App">
      {/* create a form element */}
      <form>
        {/* create an input element */}
        <input
          name="todo"
          type="text"
          placeholder="Create a new todo"
        />
      </form>

      {/* create a ul to hold all of the list items */}
      <ul className="todo-list">
        {/* map over the todos array which creates a new li element for every todo */}
        {todos.map((todo) => (
          <li>{todo}</li>
        ))}
      </ul>
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

Now we should have a simple input on the screen alt text

3. Add todo functionality

We are going to create two functions to add new todos and keep track of the input value.

import { useState } from "react";
import "./styles.css";

export default function App() {
  // need a state to keep track of todos
  const [todos, setTodos] = useState([]);
  // need state to keep track of the value in the input
  const [todo, setTodo] = useState("");

    // function to get the value of the input and set the new state
  function handleInputChange(e) {
    // set the new state value to what's currently in the input box
    setTodo(e.target.value);
  }

  // function to create a new object on form submit
  function handleFormSubmit(e) {
    // prevent the browser default behavior or refreshing the page on submit
    e.preventDefault();

    // don't submit if the input is an empty string
    if (todo !== "") {
      // set the new todos state (the array)
      setTodos([
        // copy the current values in state
        ...todos,
        {
          // setting a basic id to identify the object
          id: todos.length + 1,
          // set a text property to the value of the todo state and 
          // trim the whitespace from the input
          text: todo.trim()
        }
      ]);
    }

    // clear out the input box
    setTodo("");
  }

  return (
    <div className="App">
      {/* create a form element */}
      <form>
        {/* create an input element */}
        <input
          name="todo"
          type="text"
          placeholder="Create a new todo"
        />
      </form>

      {/* create a ul to hold all of the list items */}
      <ul className="todo-list">
        {/* map over the todos array which creates a new li element for every todo */}
        {todos.map((todo) => (
          <li>{todo}</li>
        ))}
      </ul>
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

4. Finish the functionality

Now we need to use the functions we just built to actually make something happen.

import { useState } from "react";
import "./styles.css";

export default function App() {
  // need a state to keep track of todos
  const [todos, setTodos] = useState([]);
  // need state to keep track of the value in the input
  const [todo, setTodo] = useState("");

  // function to get the value of the input and set the new state
  function handleInputChange(e) {
    // set the new state value to what's currently in the input box
    setTodo(e.target.value);
  }

  // function to create a new object on form submit
  function handleFormSubmit(e) {
    // prevent the browser default behavior or refreshing the page on submit
    e.preventDefault();

    // don't submit if the input is an empty string
    if (todo !== "") {
      // set the new todos state (the array)
      setTodos([
        // copy the current values in state
        ...todos,
        {
          // setting a basic id to identify the object
          id: todos.length + 1,
          // set a text property to the value of the todo state and 
          // trim the whitespace from the input
          text: todo.trim()
        }
      ]);
    }

    // clear out the input box
    setTodo("");
  }

  return (
    <div className="App">
      {/* create a form element and pass the handleFormSubmit function 
      to the form using the onSubmit prop */}
      <form onSubmit={handleFormSubmit}>
        {/* create an input element - make sure to add the value prop 
        with the state value passed in and the onChange prop to update
        the state every time something is typed in the input */}
        <input
          name="todo"
          type="text"
          placeholder="Create a new todo"
          value={todo}
          onChange={handleInputChange}
        />
      </form>

      {/* create a ul to hold all of the list items */}
      <ul className="todo-list">
        {/* map over the todos array which creates a new li element for every todo
        (make sure to add the "key" prop using the unique todo.id value to the li element)
        remember this is an array of objects - so we need to access the property 
        "text" to get the value we want to display */}
        {todos.map((todo) => (
          <li key={todo.id}>{todo.text}</li>
        ))}
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now you should start seeing the todos being added to the page. alt text

This is the first post in this series. Keep in mind that this is the absolute basic functionality of the app. We will be adding more functionality in the other posts.

Thanks for reading!

Latest comments (5)

Collapse
 
prathisahrudh profile image
Sahrudh Prathi

Thank you for splitting up the process, exactly as i wished for .

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
joelynn profile image
Joseph Lynn

@andreisaliba that's true. That was the simplest way to create an id to get the app started. But now that you mention it, I do think using new Date() would have actually been a better simple solution. This will be address in my 5th post when I refactor the application. Appreciate the feedback!

Collapse
 
andrewbaisden profile image
Andrew Baisden

Great post cant wait to see more.

Collapse
 
joelynn profile image
Joseph Lynn

@andrewbaisden I appreciate the feedback - thanks for checking this out!